본문 바로가기
Swift 공식 가이드/Swift 3

Inheritance

by 토끼찌짐 2017. 3. 1.

Apple 제공 Swift 프로그래밍 가이드(3.0.1)의 Inheritance 부분을 공부하며 정리한 글입니다. 개인적인 생각도 조금 들어가있습니다.



들어가며

클래스는 다른 클래스로부터 메서드, 프로퍼티, 그 외 다른 특성들을 상속inheritance받을 수 있다. 상속을 받은 클래스를 서브클래스subclass, 상속을 해준 클래스를 슈퍼클래스superclass라고 부른다. 

서브클래스는 슈퍼클래스의 메서드, 프로퍼티, 스크립트에 접근할 수 있고, 재정의(오버라이딩)할 수도 있다.

상속받은 프로퍼티에 프로퍼티 옵저버를 추가할 수 있다. 지난번 프로퍼티 포스팅에 언급했던 것처럼, 슈퍼클래스의 computed 프로퍼티에도 서브클래스에서는 옵저버를 추가할 수 있다.



Defining a Base Class

베이스 클래스(Base Class) : 어떤 클래스도 상속받지 않은 클래스

<note> 스위프트에서는 모든 클래스가 상속받아야 할 universal base class가 특별히 없다. 따라서 아무 클래스도 상속받지 않은 클래스는 자연히 베이스 클래스가 된다.

class Vehicle {
    var currentSpeed = 0.0
    var description: String {
        return "현재 스피드는 \(currentSpeed)"
    }
    
    func makeNoise() {
        // 어떤 차일지에 따라 소리가 다르다
    }
}

let someVehicle = Vehicle()
print("Vehicle : \(someVehicle.description)")
// "Vehicle : 현재 스피드는 0.0"


Subclassing

서브클래싱은 기존 클래스(superclass)를 기반으로 하여 새로운 클래스(subclass)를 만드는 것이다. 서브클래스는 슈퍼클래스로부터 특성을 상속받고, 특성을 재정의할 수 있으며, 새로운 특성을 더할 수도 있다.

class SomeSubclass: SomeSuperclass {
    // subclass definition goes here
}


위에서 정의했던 베이스 클래스 Vehicle을 서브클래싱해보자.

class Bicycle: Vehicle {
    var hasBasket = false // 서브클래스만의 새로운 특성을 더했다
}

let bicycle = Bicycle()
bicycle.hasBasket = true
bicycle.currentSpeed = 15.0 // 슈퍼클래스의 프로퍼티를 상속받음
print(bicycle.description) // 슈퍼클래스의 메서드를 상속받음
// "Vehicle : 현재 스피드는 15.0"


// 위 서브클래스를 또 서브클래싱 할 수도 있다
class Tandem: Bicycle {
    var currentNumberOfPassengers = 0
}

let tandem = Tandem()
tandem.hasBasket = true
tandem.currentNumberOfPassengers = 2
tandem.currentSpeed = 22.0
print(tandem.description)
// "Vehicle : 현재 스피드는 22.0"
// Vehicle에 있던 decription 프로퍼티를 Bicycle이 상속받았고,
// Tandem이 Bicycle을 상속받았으므로 당연히 description에 접근 가능하다



Overriding

서브클래스는 슈퍼클래스의 메서드, 프로퍼티, 서브스크립트를 재정의할 수 있는데, 이것을 오버라이딩이라고 부른다. (따로 재정의하지 않으면 슈퍼클래스의 구현체를 그대로 사용한다)

오버라이딩을 위해서는 해당 메서드, 프로퍼티, 서브스크립트의 정의 앞에 override 키워드를 붙여준다.

  • override 키워드가 붙었는데 슈퍼클래스에 정의되어 있는 것과 인터페이스가 일치하지 않으면 에러

  • override 키워드가 안붙었는데 슈퍼클래스에 정의되어 있는 것과 인터페이스가 일치하면 에러


<Accessing Superclass Methods, Properties, and Subscripts>

서브클래스 안에서 슈퍼클래스의 메서드, 프로퍼티, 서브스크립트를 오버라이드했더라도 경우에 따라서는 슈퍼클래스의 것을 사용해야 할 경우가 있다. 그럴때는 super 를 통해 접근한다.

  • super.someMethod()

  • super.somProperty

  • super[someIndex]


<Overriding Methods>

class Train: Vehicle {
    override func makeNoise() {
        print("칙폭칙폭")
    }
}

let train = Train()
train.makeNoise()
// "칙폭칙폭"


<Overriding Properties>

메서드와 마찬가지로 프로퍼티도 오버라이드할 수 있다. 또한 상속받은 프로퍼티에 옵저버를 추가할 수도 있다.


<Overriding Property Getters and Setters>

class Car: Vehicle {
    var gear = 1
    override var description: String {
        return super.description + " in gear \(gear)"
    }
}

상속받은 프로퍼티에 대해 custom getter/setter를 제공할 수 있다.

  • 즉, Computed 프로퍼티 형식으로 오버라이드하라는 것이다

  • 슈퍼클래스에서의 해당 프로퍼티가 Stored 프로퍼티여도 가능하다 (단 클래스 Stored 프로퍼티는 제외. 왜냐면 static 키워드를 붙여야만 정의할 수 있는데, static을 붙이면 오버라이드 할 수 없다.)

  • 서브클래스는 프로퍼티의 이름/타입만 알고 있으며 슈퍼클래스의 구현은 알지 못한다. 반대로 말하면 프로퍼티의 이름/타입을 명시해야 오버라이드 가능

  • 슈퍼클래스의 read-only(getter만 제공)프로퍼티를 서브클래스에서 read-writer(getter/setter 모두 제공)로 만드는 것은 가능하지만,

  • 슈퍼클래스의 read-writer를 서브클래스에서 read-only로 만드는 것은 불가능. (즉, 이 경우는 getter만 오버라이드 불가능. getter/setter 모두 정의해주어야 한다) *부모가 가진 속성을 자식은 거부하면 안 된다(OOP 기본규칙 중 추상화 개념에 위배된다)

  • Computed 프로퍼티는 setter만 정의할 수 없는 것처럼, 프로퍼티 오버라이드의 경우에도 setter만 정의할 수 없다. getter도 반드시 같이 정의되어야 한다. 오버라이드 할 부분이 없다면 super.프로퍼티이름 이라도 넣어주어야 한다.


데...

stored 프로퍼티를 이런식으로 오버라이드하는 것은 왜 못하게 하는지... why?



<Overriding Property Observers>

프로퍼티 오버라이드를 사용하면 상속받은 프로퍼티에 옵저버를 추가할 수 있다.

class Vehicle {
    var currentSpeed = 0.0
    var description: String {
        return "현재 스피드는 \(currentSpeed)"
    }
    
    func makeNoise() {
        // 어떤 차일지에 따라 소리가 다르다
    }
}

class Car: Vehicle {
    var gear = 1
    override var description: String {
        return super.description + " in gear \(gear)"
    }
}


// currentSpeed에 옵저버를 추가하기 위해 프로퍼티 오버라이드를 사용한다
class AutomaticCar: Car {
    override var currentSpeed: Double {
        didSet {
            gear = Int(currentSpeed / 10.0) + 1
        }
    }
}

let automatic = AutomaticCar()
automatic.currentSpeed = 35.0
// gear 은 4


// <추가설명> 슈퍼클래스에도 같은 프로퍼티에 옵저버가 걸려있다면
// 그것도 같이 불리게 된다. 아래 예시를 보자.

class Vehicle {
    var currentSpeed = 0.0 {
        willSet {
            print("willSet in super")
        }
    }
}

class Car: Vehicle {
    override var currentSpeed: Double {
        willSet {
            print("willSet in sub")
        }
    }
}

let car = Car()
car.currentSpeed = 3
// willSet in sub, willSet in super 둘 다 출력됨

// 위 상황에서 슈퍼클래스의 옵저버가 불리지 않게 하고 싶다면
// 프로퍼티의 setter, getter를 오버라이드 하면 된다고 하는데
// 여러가지로 시도해봤지만 상당히 코드가 더러워졌다
// 구글링을 해본 결과 다음과 같은 구조가 깔끔한 것 같다
// (http://stackoverflow.com/questions/29503482/override-property-observer)

class Vehicle {
    var currentSpeed = 0.0 {
        willSet {
            self.willCurrentSpeedChange()
        }
    }
    
    func willCurrentSpeedChange() {
        print("willSet in super")
    }
}

class Car: Vehicle {
    override func willCurrentSpeedChange() {
        print("willSet in sub")
    }
}
let car = Car()
car.currentSpeed = 3
// willSet in sub만 출력됨


<note> 다음과 같은 예외사항들이 있다.

  • 상속된 상수 Stored 프로퍼티, 상속된 read-only Computed 프로퍼티에 옵저버를 추가하는 것은 불가. (변하지 않는 값이기 때문에 불필요)

  • 프로퍼티의 setter와 옵저버를 둘 다 오버라이드 하는 것은 불가능. (setter를 오버라이드 했다면 그 안에서 값의 변화를 탐지하도록 하자)



Preventing Overrides

서브클래스에서 오버라이드를 못하게 하고 싶다면 해당 메서드, 프로퍼티, 스크립트 정의 앞에 final 키워드를 붙인다. 클래스 전체를 final로 만들고 싶다면 class 키워드 앞에 final 키워드를 붙이면 된다.

'Swift 공식 가이드 > Swift 3' 카테고리의 다른 글

Initialization (2/3)  (0) 2017.03.13
Initialization (1/3)  (0) 2017.03.12
Subscripts  (0) 2017.03.01
Methods  (0) 2017.03.01
Properties  (0) 2017.02.27