하위 클래스는 상위 클래스에서 상속할 인스턴스 메소드, 타입 메소드, 인스턴스 프로퍼티, 타입 프로퍼티, 또는 서브스크립트 자체 사용자 구현을 제공할 수 있다. 이것을 재정의(Overriding)이라 한다.
상속될 특성을 재정의하려면 재정의할 정의 앞에 overide 키워드를 추가한다. 이렇게 하면 재정의를 명확하게 제공하고 실수로 동일한 정의를 재정의하지 않도록 한다. 실수로 재정의하면 예기치 않은 동작을 제공하고, override키워드 없이 재정의하면 코드가 컴파일될 때 에러를 발생시킨다.
Swift 컴파일러는 override 키워드 사용 시 재정의한 클래스의 상위 클래스 또는 상위 클래스 중 하나에 재정의를 위해 일치하는 선언이 있는지를 체크한다. 이 체크는 재정의하는 정의가 옳다고 보장한다.
하위 클래스에 메소드, 프로퍼티 또는 서브스크립트 재정의를 제공할 때 재정의의 일부로 상위 클래스 구현을 사용하는 것이 유용할 때가 있다. 예를 들어 기존 구현의 동작을 구체화하거나 기존 상속된 변수에 수정된 값을 저장할 수 있다.
이것이 적절한 경우 super접두어를 사용하여 메소드, 프로퍼티 또는 서브스크립트의 상위 클래스 버전을 접근한다.
someMethod()라는 재정의된 메소드는 재정의한 메소드 구현 내에서 super.someMethod()를 호출하여 상위 클래스 버전의someMethod()를 호출할 수 있다.someProperty라는 재정의된 프로퍼티는 재정의한 getter 또는 setter 구현 내에서 super.someProperty로 상위 클래스 버전의 someProperty를 접근할 수 있다.someIndex를 위한 재정의된 서브스크립트는 재정의한 서브스크립트 구현 내에서 super[someIndex]로 상위 클래스 버전의 같은 서브스크립트에 접근할 수 있다.상속된 인스턴스 또는 타입 메소드를 재정의하여 하위 클래스 내에서 메소드의 맞춤형 / 대체 구현을 제공한다.
아래 예제는 Train이라는 Vehicle의 새로운 하위 클래스를 정의한다. Vehicle을 상속한 Train에 makeNoise() 메소드를 재정의한다.
class Train: Vehicle {
override func makeNoise() {
print("Choo Choo")
}
}
Train에 새로운 인스턴스를 생성하고 makeNoise() 메소드를 호출하면 Train 하위 클래스 버전의 메소드가 호출되는 것을 볼 수 있다.
let train = Train()
train.makeNoise()
// Prints "Choo Choo"
프로퍼티에 고유한 사용자 정의 getter와 setter를 제공하거나 기본 프로퍼티 값이 변경될 때 재정의한 프로퍼티가 관찰할 수 있도록 프로퍼티 관찰자를 추가하기 위해 상속된 인스턴스 또는 타입 프로퍼티를 재정의할 수 있다.
상속된 프로퍼티가 소스에서 저장된 또는 계산된 프로퍼티로 구현되었는지 여부와 상관없이, 모든 상속된 프로퍼티를 재정의하기 위해 사용자 지정 getter와 setter를 제공할 수 있다. 상속된 프로퍼티에 저장된 또는 계산된 특성은 하위 클래스에서 알 수 없다. 상속된 프로퍼티의 특정 이름과 타입만 알고 있다. 컴파일러가 동일한 이름과 타입을 가진 상위 클래스 프로퍼티와 일치하는지 확인할 수 있도록 항상 재정의하는 프로퍼티의 이름과 타입을 모두 명시해야 한다.
하위 클래스에서 getter와 setter를 모두 제공하여 상속된 읽기 전용 프로퍼티를 읽기-쓰기 프로퍼티로 표시할 수 있다. 그러나 상속된 읽기-쓰기 프로퍼티를 읽기 전용 프로퍼티로 표시할 수 없다.
Note
프로퍼티 재정의의 부분으로 setter를 제공하면 getter도 제공해야 한다. 재정의한 getter 내에서 상속된 프로퍼티의 값을 수정하고 싶지 않다면, someProperty가 재정의하는 프로퍼티의 이름이라면 getter에서 super.someProperty를 반환하면 상속된 값을 간편하게 전달할 수 있다.
다음의 예제는 Vehicle의 하위 클래스인 Car라는 새로운 클래스를 정의한다. Car 클래스는 기본 정수 값이 1인 gear라는 새로운 저장된 프로퍼티를 가진다. Car 클래스는 현재 기어를 포함한 설명을 제공하기 위해 Vehicle로부터 상속한 description 프로퍼티를 재정의한다.
class Car: Vehicle {
var gear = 1
overrider var description: String {
return super.description + " in gear \(gear)"
}
}
description 프로퍼티의 재정의는 Vehicle 클래스의 description 프로퍼티를 반환하는 super.description 호출로 시작한다. 그런 다음 description의 Car 클래스 버전은 현재 기어에 대한 정보를 추가한다.
Car 클래스의 인스턴스를 생성하고 gear와 currentSpeed 프로퍼티를 설정하면 description 프로퍼티는 Car 클래스 내에서 정의한 설명을 반환하는 것을 볼 수 있다.
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
상속된 프로퍼티에 프로퍼티 관찰자를 추가하기 위해 프로퍼티 재정의를 사용할 수 있다. 이것은 기존에 구현된 프로퍼티가 어떻든 상관없이 상속된 프로퍼티의 값이 변경될 때 알림을 받을 수 있다.
Note
상속된 저장된 프로퍼티 상수 또는 상속된 읽기 전용 계산된 프로퍼티에 프로퍼티 관찰자를 추가할 수 없다. 이 프로퍼티의 값은 설정할 수 없으므로 재정의의 부분으로willSet또는didSet구현을 제공하기에 적절하지 않다.
같은 프로퍼티에 재정의한 setter와 재정의한 프로퍼티 관찰자를 동시에 제공할 수 없다. 프로퍼티의 갑싱 변경되는 것을 관찰하기 원하고 이미 프로퍼티에 사용자화 setter를 제공하고 있다면 사용자화 setter 내에서 간단하게 값 변경을 관찰할 수 있다.
다음 예제는 Car의 하위 클래스인 AutomaticCar라는 새로운 클래스를 정의한다. AutomaticCar 클래스는 현재 속도에 기반하여 적절한 기어를 자동적으로 선택하는 자동 기어박스가 있는 자동차를 표시한다.
class AutomaticCar: Car {
override var currentSpeed: Double {
didSet {
gear = Int(currentSpeed / 10.0) + 1
}
}
}
AutomaticCar 인스턴스에 currentSpeed 프로퍼티를 설정할 때 마다 프로퍼티의 didSet 관찰자는 새로운 속도에 적절한 기어를 인스턴스의 gear 프로퍼티에 설정한다. 특히 프로퍼티 관찰자는 새로운 currentSpeed 값을 10으로 나눈 다음 가까운 정수로 내림한 다음 1을 더한 값을 기어로 선택한다. 35의 속도는 기어 4가 된다.
let automatic = AutomaticCar()
automatic.currentSpeed = 35.0
print("AutomaticCar: \(automatic.description)")
// AutomaticCar: traveling at 35.0 miles per hour in gear 4