새로 배운 점
- 다른 클래스로부터 상속받지 않은 클래스를 기본 클래스(base class)라고 한다.
- someIndex로 오버라이딩된 서브스크립트는 오버라이딩된 서브스크립트 내부에서 super[someIndex]로 슈퍼클래스의 서브스크립트에 접근할 수 있다.
Inheritance
클래스는 다른 클래스로부터 메서드, 프로퍼티, 특징들을 상속받을 수 있습니다.
다른 클래스로부터 상속을 받을 때,
상속을 받는 클래스는 subclass라고 하고, 상속을 하는 클래스는 superclass라고 합니다.
Swift의 클래스는 그 클래스의 superclass에 속하는 메서드, 속성, 서브스크립트를 호출하거나 접근할 수 있고,
메서드, 속성, 서브스크립트를 오버라이딩(overriding)할 수 있습니다.
Swift는 오버라이드(override)가 슈퍼클래스의 정의와 매칭되는지 체크해 줍니다.
클래스는 상속된 프로퍼티에 프로퍼티 옵저버(property observers)를 추가하여 프로퍼티 값이 변경됐을 때
알림을 받을 수도 있습니다.
프로퍼티 옵저버는 저장 프로퍼티, 연산 프로퍼티 상관 없이 모든 속성에 추가할 수 있습니다.
Defining a Base Class
다른 클래스로부터 상속받지 않은 클래스를 기본 클래스(base class)라고 합니다.
아래 예제는 기본 클래스인 Vehicle 클래스입니다.
makeNoise( )는 Vehicle 클래스에서는 아무 동작도 안 하고 서브클래스에서 커스터마이징 됩니다.
class Vehicle {
var currentSpeed = 0.0
var description: String {
return "traveling at \(currentSpeed) miles per hour"
}
func makeNoise() {
// do nothing - an arbitrary vehicle doesn't necessarily make a noise
}
}
Vehicle 인스턴스를 생성합니다.
let someVehicle = Vehicle()
이 인스턴스의 description 프로퍼티에 접근하여 vehicle의 현재 속도를 출력합니다.
print("Vehicle: \(someVehicle.description)")
// Vehicle: traveling at 0.0 miles per hour
Vehicle 클래스는 일반적인 특성을 정의하고,
더 유용하게 쓰려면 구체적인 종류의 차량을 정의해야 합니다.
Subclassing
서브클래싱(Subclassing)은 기존 클래스에서 새 클래스를 기초로 하는 동작을 의미합니다.
서브클래스는 기존 클래스로부터 특성들을 상속 받고 수정할 수 있습니다.
새 특성을 서브클래스에 추가할 수도 있습니다.
콜론(:)을 이용해 서브클래스가 슈퍼클래스를 가진다는 것을 표시합니다.
서브클래스: 슈퍼클래스로 작성합니다.
class SomeSubclass: SomeSuperclass {
// subclass definition goes here
}
아래 코드는 Vehicle을 슈퍼클래스로 한 Bicyle 서브클래스입니다.
class Bicycle: Vehicle {
var hasBasket = false
}
Bicycle 클래스는 자동으로 Vehicle 클래스의 currentSpeed, description, makeNoise()를 포함한 모든 특성을 가집니다.
상속받는 특성 외에 Bicycle 클래스는 새로운 프로퍼티인 hasBasket을 정의하고 기본값은 false로 설정합니다.
Bicycle 인스턴스를 생성하여 hasBasket 프로퍼티 값을 바꿀 수 있습니다.
let bicycle = Bicycle()
bicycle.hasBasket = true
상속 받은 Bicycle 인스턴스의 currentSpeed 속성을 수정하고 description 프로퍼티를 출력할 수 있습니다.
bicycle.currentSpeed = 15.0
print("Bicycle: \(bicycle.description)")
// Bicycle: traveling at 15.0 miles per hour
서브클래스로 다른 서브클래스를 생성할 수 있습니다.
다음 예제는 2인용 자전거인 Tandem 클래스 정의입니다.
class Tandem: Bicycle {
var currentNumberOfPassengers = 0
}
Tandem은 Bicycle로부터 모든 프로퍼티와 메서드를 상속 받으며,
Bicycle은 Vehicle로부터 모든 프로퍼티와 메서드를 상속 받습니다.
만약 Tandem의 새로운 인스턴스를 생성하면, 새로운 프로퍼티와 상속받은 프로퍼티로 작업을 수행할 수 있고,
Vehicle로부터 상속 받은 read-only description 프로퍼티를 사용할 수 있습니다.
let tandem = Tandem()
tandem.hasBasket = true
tandem.currentNumberOfPassengers = 2
tandem.currentSpeed = 22.0
print("Tandem: \(tandem.description)")
// Tandem: traveling at 22.0 miles per hour
Overriding
서브클래스는 슈퍼클래스로부터 상속 받은 인스턴스 메서드, 타입 메서드, 인스턴스 프로퍼티, 타입 프로퍼티, 서브클래스들을
커스텀하여 구현할 수 있습니다.
이를 오버라이딩(overriding)이라고 합니다.
override 키워드를 작성하면 오버라이딩을 할 수 있습니다.
키워드를 사용해 실수를 방지할 수 있으며, 만약 키워드가 없는 오버라이딩은 컴파일 에러가 발생합니다.
또한, override 키워드는 Swift 컴파일러에게 슈퍼클래스가 오버라이딩하는 정의와 동일한지 체크하도록 합니다.
이는 오버라이딩 정의가 올바른지 확인하는 것입니다.
Accessing Superclass Methods, Properties, and Subscripts
서브클래스에 오버라이드 메서드, 프로퍼티, 서브스크립트를 제공할 때,
오버라이딩 구현 중에서 기존 슈퍼클래스의 구현을 사용하는 것이 유용할 때가 있습니다.
예를 들어, 기존의 구현을 구체화하거나 수정된 값을 기존에 상속된 변수에 저장할 수 있습니다.
슈퍼클래스 버전의 메서드, 프로퍼티, 서브스크립트는 super 키워드를 통해 접근할 수 있습니다.
- 오버라이딩 메서드 내부에서 super.someMethod()를 호출하여 슈퍼클래스의 someMethod()를 호출할 수 있습니다.
- 오버라이딩된 프로퍼티는 오버라이딩된 getter/setter 내부에서 super.someProperty를 사용하여 슈퍼클래스의 someProperty에 접근할 수 있습니다.
- someIndex로 오버라이딩된 서브스크립트는 오버라이딩된 서브스크립트 내부에서 super[someIndex]로 슈퍼클래스의 서브스크립트에 접근할 수 있습니다.
Overriding Methods
상속된 인스턴스 또는 타입 메서드를 오버라이딩하여 서브클래스 내부에서 새롭게 구현할 수 있습니다.
다음 예제는 Vehicle로부터 상속받은 makeNoise()를 오버라이드하는 Train 서브클래스를 정의합니다.
class Train: Vehicle {
override func makeNoise() {
print("Choo Choo")
}
}
Train의 새로운 인스턴스를 생성하고 이 인스턴스의 makeNoise 메서드를 호출하면,
Train 서브클래스 버전의 메서드가 호출됩니다.
let train = Train()
train.makeNoise()
// Prints "Choo Choo"
Overriding Properties
상속받은 인스턴스 프로퍼티 또는 타입 프로퍼티를 오버라이드하여
그 프로퍼티의 커스텀 getter/setter를 만들거나, 프로퍼티 옵저버를 추가할 수도 있습니다.
Overriding Property Getters and Setters
서브클래스에서는 저장 프로퍼티, 연산 프로퍼티 상관 없이
어떠한 상속된 프로퍼티를 오버라이드하여 커스텀 getter/setter를 제공할 수 있습니다.
상속된 프로퍼티가 저장 프로퍼티인지 연산 프로퍼티인지는 서브클래스에서 알 수 없으며,
오직 프로퍼티의 이름과 타입만 알 수 있습니다.
반드시 오버라이딩하려는 프로퍼티의 이름과 타입을 명시해야 합니다.
(컴파일러가 오버라이드된 것이 슈퍼클래스와 일치하는지 확인하기 때문)
상속받은 속성이 read-only라면 getter/setter를 모두 제공해 read-write 속성으로 오버라이딩할 수 있습니다.
하지만 반대의 경우는 불가능합니다.
아래 예제 코드는 Vehicle의 서브클래스인 Car 클래스를 정의합니다.
Car 클래스는 새로운 저장 프로퍼티인 gear를 정의하고 1로 초기화합니다.
description 프로퍼티를 오버라이드하여 기어 정보를 추가하여 출력합니다.
class Car: Vehicle {
var gear = 1
override var description: String {
return super.description + " in gear \(gear)"
}
}
만약 Car 클래스의 인스턴스를 생성하고 인스턴스의 gear와 currentSpeed 속성을 설정하면,
description 속성이 아래와 같이 출력하는걸 볼 수 있습니다.
let car = Car()
car.currentSpeed = 25.0
car.gear = 3
print("Car: \(car.description)")
// Car: traveling at 25.0 miles per hour in gear 3
Overriding Property Observers
상속받은 프로퍼티에 프로퍼티 옵저버를 추가하여 오버라이딩할 수 있습니다.
프로퍼티 옵저버에 대해서는 아래 글을 참고해주세요.
상속된 상수 저장 프로퍼티나 read-only 연산 프로퍼티에는 프로퍼티 옵저버를 추가할 수 없습니다.
이들의 값은 절대 설정될 수 없기 때문에 willSet과 didSet을 오버라이드하는 것은 적절하지 않습니다.
또한, 동일한 프로퍼티에 대해 setter와 프로퍼티 옵저버를 동시에 제공할 수 없습니다.
이미 setter가 정의되어 있다면, setter에서 값의 변화를 알아챌 수 있습니다.
아래 예제 코드는 Car의 서브클래스인 AutomaticCar 클래스를 정의합니다.
class AutomaticCar: Car {
override var currentSpeed: Double {
didSet {
gear = Int(currentSpeed / 10.0) + 1
}
}
}
AutomaticCar 인스턴스의 currentSpeed 값을 설정할 때마다
didSet 옵저버는 인스턴스의 gear 값을 수정합니다.
let automatic = AutomaticCar()
automatic.currentSpeed = 35.0
print("AutomaticCar: \(automatic.description)")
// AutomaticCar: traveling at 35.0 miles per hour in gear 4
Preventing Overrides
메서드, 프로퍼티, 서브스크립트를 final로 표시하면 오버라이딩이 불가능합니다.
final 수식어를 메서드, 프로퍼티, 서브스크립트 앞에 작성하면 됩니다.
(ex. final var, final func, final class func, and final subscript)
서브클래스에서 final 메서드, 프로퍼티, 서브스크립트를 오버라이딩하면 컴파일 에러가 발생합니다.
클래스를 정의할 때 class 키워드 앞에 final을 붙이면 클래스를 상속할 수 없습니다.
만약 final class를 상속하게 되면 컴파일 에러가 발생합니다.
참고
https://docs.swift.org/swift-book/LanguageGuide/Inheritance.html