Swift

[Swift] Initialization (2) | 상속, 확장에서의 생성자

하이D:) 2023. 10. 10. 16:33

앞의 글에서 알아본 것처럼 생성자의 종류는 많습니다..!! 이번에는 상속의 경우에 지정생성자와 편의생성자가 어떻게 재정의 되는지와 확장의 경우에 생성자를 어떤 식으로 구현할 수 있는지에 대해서 정리해보려구 해요!

 

 

[Swift] Initialization (1) | 생성자의 종류 : 지정생성자, 편의생성자, 필수생성자, 실패가능생성자

[Swift] Initialization (2) | 상속, 확장에서의 생성자

 

 

# 🥨 상속에서의 생성자

필요한 경우 우리는 상속을 종종하곤하는데 이때 메서드 등 여러 가지를 재정의(Override)하곤 하죠? 이것처럼 생성자도 재정의가 필요한데 이 과정이 좀 까다로울 수 있어서 한번 정리해 보려고요.

 

 

지정 생성자의 재정의

일단, 지정생성자는 서브클래스에서 재정의(override)하는 게 원칙!! 왜일까?

  • 상속 후 저장 속성을 추가해 줄 가능성이 높기 때문
  • 이 경우, 생성자는 모든 속성을 초기화하는 도구인데 상위의 생성자는 서브클래스에 최적화되어 있지 않기 때문

 

편의 생성자의 재정의

위에서 본 것처럼 지정생성자는 서브클래스에서 재정의(override)하는 게 원칙이라면 편의 생성자는 서브클래스에 상속되지 않는 게 원칙!!이다! 왜일까?

  • 편의 생성자는 궁극적으로 같은 단계의 지정생성자를 호출하기 때문
  • 상속의 경우에, 본인 단계의 저장속성과 지정생성자에 대한 정보만 명확히 담고 있으면 되기 때문에 편의 생성자는 하위, 상위 클래스에 영향을 받지 않는 것이다.

 

 

# 🥨  상속에서의 자동 상속

물론, 재정의하지 않아도 자동 상속되는 지정생성자의 경우가 있고, 원칙적으로 상속되거나 재정의 하지 않지만 자동 상속되는 편의 생성자의 경우가 있긴 하다.

 

지정 생성자가 자동상속되는 경우

서브클래스에 새로운 저장속성이 아얘 없거나, 서브클래스에서 기본값 다 설정한 경우에 & 서브클래스에서 상위 클래스 생성자에 대해 어떠한 재정의도 안 했을 때 (서브클래스 인스턴스 생성 시 새로 설정해 줄 저장 속성이 없기 때문에 실패 가능성이 없다! )

class SomeClass {
    var a : String
    var b : String
    let c : String

    //생성자
    init(a:String, b:String, c:String){
        self.a = a
        self.b = b
        self.c = c
    }
}

class SubClass : SomeClass {
    var d : String = "d" // 이 경우 지정생성자를 재정의할 필요가 없다
}

 

편의 생성자가 자동상속 되는 경우

원래 편의생성자는 원칙적으로 상속되지 않고재정의 하면 안 된다고했지만 예외적으로 자동상속되는 경우가 있긴 하다.

  • 상위클래스의 지정 생성자가 모두 자동 상속되는 경우 ( 위에서 설명한 지정생성자가 자동상속 되는 경우가 이에 해당하며, 저장속성 기본값 설정해 놨고 어떤 재정의도 하지 않았을 때를 말한다)
  • 혹은, 상위의 지정 생성자가 모두 재정의 되는 경우 

 

 

# 🥨  지정생성자를 재정의하는 세 가지 방법

상속에서의 생성자의 재정의의 경우에 슈퍼클래스에서의 지정생성자가반드시 서브클래스에서의 지정생성자로 재정의 될 필요는 없고 아래의 가지 방법으로 구현해줄 수 있는데 우리가 궁극적으로지켜야 하는델리게이트 어크로스, 델리게이트 원칙만 지켜주면서구현해 주면 된다!

  • 상위 클래스 지정생성자 -> 상속 후 지정생성자로 재정의 가능!
  • 상위 클래스 지정생성자 -> 상속 후 편의생성자로 재정의 가능!
  • 상위 클래스 지정생성자 -> 자동상속되는 경우(위에서 설명)에는 재정의 하지 않아도 된다!

 

# 🥨  델리게이트 어크로스, 델리게이트 업

위에서 생성자의 상속 및 재정의 방법과 여러 예외 상황들을 봐서 머리가 아플 텐데 우리가 반드시 기억하고 생성자의 상속에서 궁극적으로 구현되어야 하는 것은 바로 델리게이트 어크로스, 델리게이트 업이다. 이 두 가지만 알고 있으면 복잡한 머릿속이 조금이나마 정리될 것이다.

 

델리게이트 어크로스

: 편의 생성자는 동일한 클래스에서 다른 편의 생성자 또는 동일한 클래스에서의 지정생성자를 호출해야 하며, 궁극적으로는 본인 단계의 지정생성자를 호출해야 한다.

 

델리게이트 업

: 서브클래스에서의 지정생성자슈퍼클래스의 지정생성자를 반드시 호출해야 한다.

 

 

# 🥨 확장에서의 생성자

확장 extension에서의 생성자의 경우엔 상속보다는 비교적 간단한 원칙을 가지고 구현하면 된다.

 

클래스class 확장에서의 생성자

클래스 확장에서 생성자는 편의 생성자만 된다! 왜일까?

 

클래스의 지정 생성자는 반드시 본체에 있어야 하기 때문에 extension에서는 본체에 있는 지정생성자를 호출하는 편의 생성자만 추가할 수 있다.

 

구조체struct 확장에서의 생성자

구조체에서의 확장에서는 생성자에 대한 제한이 없다. 왜일까?

 

본체에 지정생성자 반드시 있어야 할 필요 없기 때문에 확장에서도 지정생성자 구현이 가능하며, 구조체 확장에서 생성자를 구현했을 경우에도 기본생성자 + 멤버와이즈 생성자 모두 기본제공한다! ( 확장에서 생성자를 직접 구현했어도 본체에서 생성자를 구현하지 않았으면 기본 + 멤버와이즈 생성자 제공)

struct SomeType {
    var a : Int = 1
    var b : Int = 2
}

extension SomeType {
    init(a : Int) {
        self.a = a
    }
}

let some1 = SomeType() //기본 생성자
let some2 = SomeType(a: 3)
let some3 = SomeType(a:3, b:4) // 멤버와이즈 생성자

print(some1)
print(some2)
print(some3)