大器晩成

클래스의 지정, 편의 생성자의 상속과 재정의 본문

iOS/Swift 문법

클래스의 지정, 편의 생성자의 상속과 재정의

zerobugpark 2024. 12. 6. 20:04

 

생성자 상속 시 구현 규칙

  • 상위의 지정생성자(이름/파라미터)와 현재단계의 저장속성을 고려해서 구현합니다.

 

[1단계 - 상위 생성자에 대한 고려]

  • 상위에 어떤 지정 생성자가 존재하는지?
(상위) 지정 생성자 1) 하위클래스에서 지정 생성자로 구현 (재정의)
2) 하위클래스에서 편의 생성자로 구현 가능 (재정의)
3) 구현 안 해도 됨(반드시 재정의하지 않아도 됨)
(상위) 편의 생성자 재정의를 하지 않아도 됨 (호출 불가가 원칙이기 때문에 재정의 제공 안 함)
(만약에 동일한 이름을 구현했다면) 그냥 새로 정의한 것임

 

[2단계 - (현재단계의) 생성자 구현]

 1) 지정 생성자 내에서 1) 나의 모든 저장 속성을 초기화해야 함
2)  슈퍼 클래스의 지정 생성자 호출
2) 편의 생성자 내에서 현재 클래스의 지정생성자 호출 해야 함 (편의 생성자를 거치는 것은 상관없음)
결국 지정 생성자만 모든 저장 속성을 초기화 가능

 

class Monster {
    var hp = 10
    
    // 기본생성자가 자동으로 제공됨
    //init() { }
    
}

let snail = Monster()
print("달팽이의 체력은 \(snail.hp)")


----------------------

//상위 클래스의 생성자 종류
// init()

class Monster {
    var hp = 10
    
    // 기본생성자가 자동으로 제공됨
    //init() { }
    
}

let snail = Monster()
print("달팽이의 체력은 \(snail.hp)")


class Boss: Monster {
    var skils: String
    
    // 1. 상위의 지정생성자를 고려
    // init()
    
    // 선택1. 지정생성자로 재정의
    // 상위클래스의 이름이 동일한 생성자 구현은 재정의만 가능
//    override init() {
//        // 1.나의 모든 저장속성 초기화
//        self.skils = "Lumos"
//        //2.  슈퍼 클래스의 지정생성자 호출
//        super.init()
//    }
    
    
    // 선택2 하위클래스에서 편의생성자로 구현
    // 상위클래스의 이름이 동일한 생성자 구현은 재정의만 가능
    override convenience init() {
        //편의생성자를 구현했기 때문에 지정생성자 호출
        self.init(skils: "Lumos")
    }
    
    // 2. 현재단계의 생성자 구현
    init(skils: String) {
        self.skils = skils
        super.init()
    }
    
    // 선택3 재정의를 하지 않을 수도 있음
    
}
  • 하위 클래스에서 지정생성자와 슈퍼클래스의 생성자를 지정생성자로 재정의한  구조

출처(공식문서): https://docs.swift.org/swift-book/documentation/the-swift-programming-language/initialization/#Setting-Initial-Values-for-Stored-Properties

 

 

 

애플 공식 문서 예제

class Vehicle {
    
    var numberOfWheels = 0
    
    var description: String {
        return "\(numberOfWheels) wheel(s)"
    }
    
    // init() { }
}

// numberOfWheels의 저장 속성에 기본값을 제공, 생성자 구현하지 않았으므로
// ===> 기본 생성자 init() 자동제공


let vehicle = Vehicle()
print("Vehicle: \(vehicle.description)")    // Vehicle: 0 wheel(s)


// 상위의 지정생성자 
// init()


// 서브클래스 정의
class Bicycle: Vehicle {
    
    override init() {
        super.init()            // 수퍼클래스 호출 반드시 필요
        numberOfWheels = 2      // 초기화의 2단계 값설정
    }
    
}


// 커스텀 init() 정의 ===> 생성자가 상위클래스의 이름과 동일 하므로 재정의 키워드 필요
// 상위 지정생성자 호출하여 메모리 초기화 후, 상위에서 구현한 속성에 접근하여 2로 셋팅(2단계 값설정)

let bicycle = Bicycle()
print("Bicycle: \(bicycle.description)")    // Bicycle: 2 wheel(s)


// 상위의 지정생성자 
// init()

// 서브클래스 정의
class Hoverboard: Vehicle {
    
    var color: String
    
    // (읽기) 계산 속성 재정의
    override var description: String {
        return "\(super.description) in a beautiful \(color)"
    }
    
    //    override init() {           //오버라이딩 가능
    //        self.color = "파란색"
    //        super.init()
    //    }
    //    override convenience init() { // 편의 생성자는 나의 지정생성자 호출
    //        self.init(color: "파란색")
    //    }
    

    init(color: String) {       // 저장속성이 있기 때문에, 새로운 지정생성자 생성 필요
        self.color = color      // (현재 클래스) 저장 속성 초기화
        //super.init()          // =====> 여기서 암시적으로 호출됨
    }

    
}


// 생성자에서 Hoverboard 클래스는 색상 속성만 설정
// 2단계 값설정(커스텀)없고, 상위구현에 기본 init() 만 있는 경우 생략가능(암시적 요청)


let hoverboard = Hoverboard(color: "silver")
print("Hoverboard: \(hoverboard.description)")     // Hoverboard: 0 wheel(s) in a beautiful silver

 

 

 

생성자의 상속/재정의 정리

  • 하위클래스에서는 기본적으로 상위클래스의 생성자를 상속하지 않고, 재정의하는 것이 원칙입니다.
  • 올바르게 초기화되지 않을 수 있는 가능성을 배제하는 것입니다. - 하위클래스에 최적화가 안되어 있음.
    -> 예외적으로, 안전한 경우에만 상위 클래스의 생성자가 자동으로 상속됩니다.
  • 재정의란 동일한 이름을 가진 생성자를 구현하는 것입니다.
  • 하위클래스의 커스텀 생성자 구현하기 전에 (상위클래스의) 재정의 생성자를 작성해야 실수하지 않습니다.
728x90