TIL: 지정 생성자 / 편의 생성자 상속의 예외사항

Royce·2025년 3월 23일

Swift 문법

목록 보기
36/63

지정 생성자와 편의 생성자의 자동 상속 규칙 (예외 상황)

지정 생성자 자동 상속의 예외 상황

  • 하위 클래스에서 새로운 저장 속성이 추가되지 않았거나, 모든 저장 속성이 기본값으로 초기화된 경우
  • 이 경우, 상위 클래스의 모든 지정 생성자가 자동으로 상속된다
  • 하위 클래스에서 지정 생성자를 정의하거나 재정의하지 않으면, 자동 상속이 발생한다

편의 생성자 자동 상속의 예외 상황

  • 지정 생성자가 자동으로 상속된 경우 (super.init()이 잘 동작하는 경우)
  • 하위 클래스에서 상위 클래스의 모든 지정 생성자를 재정의하여 초기화 실패의 가능성이 없어진 경우
  • 상위 클래스의 지정 생성자가 모두 상속되면, 상위 클래스의 편의 생성자도 자동으로 상속된다

지정 생성자와 편의 생성자의 상속 예외 예제

// 기본 클래스 정의 (Food)
class Food {
    var name: String  // 저장 속성 선언 (필수 초기화 대상)

    // 지정 생성자 (모든 저장 속성을 초기화한다)
    init(name: String) {     
        self.name = name  // 전달받은 파라미터로 저장 속성을 초기화한다
    }
    
    // 편의 생성자 (지정 생성자를 호출하여 초기화한다 - Delegate Across)
    convenience init() {     
        self.init(name: "[Unnamed]")  // 지정 생성자를 호출하여 name을 초기화한다
    }
}

// Food 클래스 테스트
let food1 = Food(name: "Pasta")     // 지정 생성자를 사용하여 초기화
let food2 = Food()                   // 편의 생성자를 사용하여 초기화

print("Food1: \(food1.name)")        // 출력: Food1: Pasta
print("Food2: \(food2.name)")        // 출력: Food2: [Unnamed]
  • Food 클래스는 name이라는 저장 속성을 가지고 있다
  • name은 필수적으로 초기화되어야 하므로 지정 생성자에서 초기화된다
  • convenience init() 편의 생성자는 지정 생성자를 호출하여 초기화 작업을 간편하게 한다
  • 편의 생성자는 지정 생성자를 호출하는 방식으로 초기화 위임을 수행한다
  • 이 클래스는 상속 시 특별한 예외사항이 없지만, 편의 생성자는 자동 상속되지 않는다

지정 생성자 재정의 및 편의 생성자 상속 예외 예제

// 하위 클래스 정의 (RecipeIngredient - Food 상속)
class RecipeIngredient: Food {
    var quantity: Int  // 새로운 저장 속성 추가

    // 지정 생성자 - 모든 저장 속성을 초기화한다 (상위 클래스의 지정 생성자를 호출)
    init(name: String, quantity: Int) {  
        self.quantity = quantity          // 새로운 저장 속성을 초기화한다
        super.init(name: name)            // 상위 클래스의 지정 생성자를 호출 (Delegate Up)
    }
    
    // 지정 생성자를 편의 생성자로 재정의 (Override)
    override convenience init(name: String) {   
        self.init(name: name, quantity: 1)  // 지정 생성자를 호출하여 초기화 (Delegate Across)
    }
}

// RecipeIngredient 클래스 테스트
let ingredient1 = RecipeIngredient()
let ingredient2 = RecipeIngredient(name: "Tomato")
let ingredient3 = RecipeIngredient(name: "Egg", quantity: 12)

print("Ingredient1: \(ingredient1.name), Quantity: \(ingredient1.quantity)")  // 출력: Ingredient1: [Unnamed], Quantity: 1
print("Ingredient2: \(ingredient2.name), Quantity: \(ingredient2.quantity)")  // 출력: Ingredient2: Tomato, Quantity: 1
print("Ingredient3: \(ingredient3.name), Quantity: \(ingredient3.quantity)")  // 출력: Ingredient3: Egg, Quantity: 12
  • RecipeIngredient 클래스는 Food 클래스를 상속받아 새로운 저장 속성 quantity를 추가한다
  • quantity는 새로 추가된 저장 속성이므로 자동 상속 규칙이 깨진다
  • 지정 생성자 (init(name: String, quantity: Int))를 정의하여 quantity를 초기화해야 한다
  • 상위 클래스의 지정 생성자를 호출하기 위해 (super.init()) 반드시 사용해야 한다
  • 지정 생성자 init(name:)을 편의 생성자로 재정의하여 초기화 과정을 단순화한다
  • 이 클래스는 상속 시 자동 상속되지 않는다 (quantity 초기화 필요)

지정 생성자 및 편의 생성자의 자동 상속 예제

자동 상속 예제 (기존의 지정 생성자 자동 상속)

// 하위 클래스 정의 (ShoppingListItem - RecipeIngredient 상속)
class ShoppingListItem: RecipeIngredient {
    var purchased = false  // 새로운 저장 속성 (기본값 제공)
    
    var description: String {
        var output = "\(quantity) x \(name)"
        output += purchased ? " ✔" : " ✘"
        return output
    }
}

// ShoppingListItem 클래스 테스트 (자동 상속 확인)
let item1 = ShoppingListItem()  // 편의 생성자 사용 (자동 상속)
let item2 = ShoppingListItem(name: "Bread")  // 편의 생성자 사용 (자동 상속)
let item3 = ShoppingListItem(name: "Milk", quantity: 2)  // 지정 생성자 사용

item1.purchased = true  // 초기화 후 저장 속성 값 변경 가능

print(item1.description)  // 출력: 1 x [Unnamed] ✔
print(item2.description)  // 출력: 1 x Bread ✘
print(item3.description)  // 출력: 2 x Milk ✘
  • ShoppingListItem 클래스는 RecipeIngredient 클래스를 상속한다.
  • 새로운 저장 속성 purchased는 기본값 false로 초기화된다
  • 기본값이 제공되기 때문에 초기화의 실패 가능성이 없다
  • 따라서, 상위 클래스의 지정 생성자 및 편의 생성자가 모두 자동으로 상속된다
  • 자동 상속이 발생하는 이유
    • 새로운 저장 속성 purchased가 기본값으로 초기화됨
    • 상위 클래스의 지정 생성자가 자동으로 상속될 수 있음

상위의 지정 생성자를 모두 재정의하는 경우

class FullyDefinedShoppingListItem: RecipeIngredient {
    var purchased: Bool

    // 모든 지정 생성자를 재정의함 (상위의 init(name:), init(name:quantity:) 전부 재정의)
    override init(name: String, quantity: Int) {
        self.purchased = false
        super.init(name: name, quantity: quantity)
    }
    
    override init(name: String) {
        self.purchased = false
        super.init(name: name)
    }
}

// FullyDefinedShoppingListItem 클래스 테스트 (지정 생성자 재정의 확인)
let itemA = FullyDefinedShoppingListItem(name: "Apple", quantity: 5)
let itemB = FullyDefinedShoppingListItem(name: "Orange")

print("Item A: \(itemA.name), Quantity: \(itemA.quantity), Purchased: \(itemA.purchased)")  // 출력: Apple, 5, false
print("Item B: \(itemB.name), Quantity: \(itemB.quantity), Purchased: \(itemB.purchased)")  // 출력: Orange, 1, false
  • FullyDefinedShoppingListItem 클래스는 RecipeIngredient 클래스를 상속한다
  • 모든 지정 생성자를 재정의하여 초기화한다
  • 이 경우, 상위 클래스의 지정 생성자가 자동 상속되지 않기 때문에 직접 구현해야 한다
  • 상위 클래스의 지정 생성자가 모두 재정의되면, 상위 클래스의 편의 생성자가 자동으로 상속된다

요약

  • 새로운 저장 속성을 추가하지 않거나 모든 저장 속성이 기본값으로 초기화되면, 지정 생성자가 자동으로 상속된다
  • 상위 클래스의 지정 생성자를 모두 재정의하면, 상위 클래스의 편의 생성자가 자동으로 상속된다
  • 자동 상속은 초기화 실패의 가능성이 없을 때만 발생한다
profile
iOS 개발자 지망생

0개의 댓글