Swift 상속

  1. 상속이란? 기존(부모) 클래스에 구현된 변수(프로퍼티)/메소드를 상속받은 새로운(자식) 클래스가 그대로 사용가능한 상태를 의미한다.
  2. Swift 클래스 상속
    1. 클래스의 상속은 단일 상속만 허용한다. 즉 자식 클래스는 하나의 부모 클래스만 상속한다.
    2. 문법
      클래스 상속 문법
      class Child Class Name : Parent Class Name {
      }
  3. 클래스 상속과 포인터
    1. self, super
      1. self 포인터는 현재 자신의 클래스 내에 있는 프로퍼티/메소드에 접근 시에 사용한다.
      2. super 포인터는 자신이 상속하고 있는 부모 클래스의 프로퍼티/메소드 접근 시에 사용한다.
      3. self 포인터와 super 포인터 사용 예시는 아래와 같다.
        self 포인터와 super 포인터
        // 부모 클래스
        class Parent {
           func description() -> String { return "Parent Class" }
        }

        // 자식 클래스
        class Child {
            // 메소드 재정의
            override func description() -> String {
                return "Child Class"
            }

            func printDescription() {
                print("super.description : \(super.description())")
                // "Parent Class" 출력
                print("self.description : \(self.description())")
                // "Child Class" 출력
            }
        }
  4. 클래스 상속과 재정의
    1. 클래스 상속은 부모 클래스의 프로퍼티/메소드를 자식 클래스에서 그대로 사용 가능하다.
    2. 메소드 재정의
      1. 부모 클래스에 있는 상속받은 메소드(같은 이름의 메소드)를 자식 클래스에서 다른 동작을 하도록 다시 정의하는 것을 메소드 재정의 라고 한다.
      2. 메소드 재정의는 메소드 앞에 override 키워드를 이용하여 재정의한다.
        메소드 재정의
        // 부모 클래스
        class Parent {
            var value = 0
             func hello() {
                print("Hello Parent Class")
             }
        }

        // 자식 클래스
        class Child : Parent { // Parent 클래스 상속
            override func hello() { // 동일 이름의 다른 동작
               print("Hello Child Class")
            }
        }

    3. 프로퍼티 재정의
      1. 부모 클래스에 정의된 프로퍼티를 재정의 한다.
        1. 프로퍼티 재정의 시에도 메소드 재정의와 동일하게 앞에 override 키워드를 이용한다.
        2. 저장 프로퍼티 재정의
          1. 방법 : willSet/didSet 행위를 추가해준다.
            저장 프로퍼티 재정의
            // 사각형의 부모 클래스
            class Rectangle {
                var width = 0
                var height = 0
                var size : Int {
                    get{return width * height}
                }
            }

            // 정사각형의 자식 클래스
            class Square : Rectangle { // Rectangle 클래스를 상속
                  override var width : Int {
                      didSet(newValue) {
                          // 새로 추가되는 행위
                          self.width += newValue
                      }
                  }
            }
        3. 계산 프로퍼티 재정의
          1. 방법 : get/set 행위를 재정의해준다.
            계산 프로퍼티 재정의
            // 사각형의 부모 클래스
            class Rectangle {
                var width = 0
                var height = 0
                var size : Int {
                    get{return width * height}
                }
            }

            // 정사각형의 자식 클래스
            class Square : Rectangle { // Rectangle 클래스를 상속
                  override var size : Int {
                      get {
                             height = width
                             return width * height
                      }
                  }
            }
    4. 재정의 시 주의사항
      1. 재정의가 필요한 프로퍼티/메소드에 override 키워드 누락 시에 에러 발생
      2. 재정의하는 대상이 아닌 프로퍼티/메소드에 override 키워드 붙이는 경우 에러 발생
      3. 부모 클래스의 프로퍼티/메소드 앞에 final 키워드가 붙은 경우는 재정의가 금지된 것으로 자식 클래스에서 재정의 할 수 없다.
  5. 클래스 상속과 초기화
    1. 자식 클래스는 부모 클래스의 프로퍼티를 상속하게 된다.
    2. 부모 클래스에 초기화가 필요한 프로퍼티가 있는 경우, 자식 클래스에서의 초기화는 아래와 같다.
      1. 자식 클래스에 Designated Initializer 가 없는 경우 부모 클래스의 Designated, Convenience Initializer 모두 상속한다.
        부모 클래스의 Initializer 상속
        // 부모 클래스
        class Parent {
            var a : Int
            init(a : Int) {
                self.a = a
            }
            convenience init() {
                self.init(a : 10)
            }
        }

        // 자식 클래스
        class Child : Parent {
            // 부모 클래스에 있는 Initializer 를 모두 상속
        }

        // 객체 생성
        var ChildObj1 = Child(a : 20) // Designated Initializer 상속
        var ChildObj2 = Child() // Convenience Initializer 상속
      2. 자식 클래스에 Designated Initializer 가 있는 경우 부모 클래스의 Designated, Convenience Initializer 모두 상속하지 못한다.
        부모 클래스의 Initializer 상속 못함
        // 부모 클래스
        class Parent {
            var a : Int
            init(a : Int) {
                self.a = a
            }
            convenience init() {
                self.init(a : 10)
            }
        }

        // 자식 클래스
        class Child : Parent {
            var b : Int
            init(a : Int, b : Int) {
                self.b = b
                super.init(a : a)
            }
            /* 자식 클래스에 Designated Initializer 가 있는 경우 부모 클래스의 Initializer 를 상속받지 못함 */
        }

        // 객체 생성
        // 자식 클래스의 Designated Initializer
        var ChildObj1 = Child(a : 20, b : 30)
        // 부모 클래스의 Initializer 를 상속 받지 못함
        var ChildObj2 = Child(a : 20)
        var ChildObj3 = Child()
      3. 자식 클래스에서 Designated Initializer 를 정의하지 않고, Convenience Initializer를 부모 클래스의 Designated Initializer를 상속
        부모 클래스의 Designated Initializer 상속
        // 부모 클래스
        class Parent {
            var a : Int
            init(a : Int) {
                self.a = a
            }
            convenience init() {
                self.init(a : 10)
            }
        }

        // 자식 클래스
        class Child : Parent {
            var b : Int
            convenience init(a : Int, b : Int) {
                self.b = b
                super.init(a : a)
            }
            /* 자식 클래스에 Designated Initializer 가 없고 Convenience Initializer 만 정의한 경우 부모 클래스의 Initializer를 모두 상속 */
        }

        // 객체 생성
        // 자식 클래스의 Designated Initializer
        var ChildObj1 = Child(a : 20, b : 30)
        // 부모 클래스의 Initializer 를 상속 받음
        var ChildObj2 = Child(a : 20)
        var ChildObj3 = Child()
      4. 자식 클래스에 부모 클래스에 있는 모든 Designated Initializer를 재정의한 경우, Convenience Initializer 를 상속한다.
        부모 클래스의 Designated Initializer 모두 상속
        // 부모 클래스
        class Parent {
            var a : Int
            init(a : Int) {
                self.a = a
            }
            convenience init() {
                self.init(a : 10)
            }
        }

        // 자식 클래스
        class Child : Parent {
            override init(a : Int) {
                self.b = 20
                super.init(a : a)
            }
            /* 부모 클래스에 Designated Initializer 를 모두 재정의한 경우 부모 클래스의 Convenience Initializer 를 상속 */
        }

        // 객체 생성
        var ChildObj = Child()
        // 부모 클래스의 Convenience Initializer 상속
    3. 클래스 초기화 관계는 아래와 같다.
    4. 상속과 Failable Initializer
      1. 부모 클래스의 Failable Initializer를 자식 클래스가 위임한다.
      2. 자식 클래스에서 부모 클래스의 Failable Initializer 재정의하는 방법은 아래와 같다.
        1. Failable Initializer를 Failable Initializer로 재정의하는 방법
          Failable Initializer를 Failable Initializer로 재정의
          // 부모 클래스
          class Parent {
             var value : Int
             // Failable Initializer
             init?(value : Int) {
                if(value < 0) {
                    return nil
                }
                self.value = value
             }
             // Non-Failable Initializer
             init() {
                 self.value = 0
             }
          }

          // 자식 클래스
          class Child : Parent {
              override init?(value : Int) {
                  // Failable Initializer로 위임
                  super.init(value : value)
              }
          }
        2. Failable Initializer를 Non-Failable Initializer로 재정의하는 방법
          Failable Initializer를 Non-Failable Initializer로 재정의
          // 부모 클래스
          class Parent {
             var value : Int
             // Failable Initializer
             init?(value : Int) {
                if(value < 0) {
                    return nil
                }
                self.value = value
             }
             // Non-Failable Initializer
             init() {
                 self.value = 0
             }
          }

          // 자식 클래스
          class Child : Parent {
              override init(value : Int) {
                  // Non-Failable Initializer로 위임
                  super.init()
              }
          }
        3. Non-Failable Initializer를 Failable Initializer로 재정의는 할 수 없다.