Inheritance
- 클래스는 다른 클래스의 메서드, 프로퍼티 및 기타 특성을 상속할 수 있습니다.
- 한 클래스가 다른 클래스에서 상속할 때 상속하는 클래스를 하위 클래스라고 하고 상속받은 클래스를 상위 클래스라고 합니다.
- 상속은 Swift의 다른 유형과 클래스를 구별하는 기본적인 동작입니다.
- Swift의 클래스는 상위 클래스에 속하는 메서드, 프로퍼티 및 하위 첨자를 호출하고 액세스할 수 있으며 이러한 메서드, 프로퍼티 및 하위 첨자의 자체 재정의 버전을 제공하여 동작을 개선하거나 수정할 수 있습니다.
- Swift는 재정의 정의에 일치하는 슈퍼클래스 정의가 있는지 확인하여 재정의가 올바른지 확인하는 데 도움이 됩니다.
- 클래스는 프로퍼티 값이 변경될 때 알림을 받기 위해 상속된 프로퍼티에 프로퍼티 관찰자를 추가할 수도 있습니다.
- 프로퍼티 관찰자는 원래 저장 프로퍼티 또는 계산 프로퍼티으로 정의되었는지 여부에 관계없이 모든 프로퍼티에 추가할 수 있습니다.
📌 Defining a Base Class
- 다른 클래스에서 상속하지 않는 모든 클래스를 기본 클래스라고 합니다.
Swift 클래스는 범용 기본 클래스에서 상속하지 않습니다. 슈퍼클래스를 지정하지 않고 정의한 클래스는 자동으로 기반 클래스가 됩니다
- 아래 예제에서는 Vehicle이라는 기본 클래스를 정의합니다.
- 이 기본 클래스는 기본값이 0.0인 currentSpeed라는 저장 프로퍼티을 정의합니다(Double 프로퍼티 유형 추론).
- currentSpeed 프로퍼티의 값은 차량에 대한 설명을 생성하기 위해 description이라는 읽기 전용으로 계산된 String 프로퍼티에 의해 사용됩니다.
- Vehicle 기본 클래스는 makeNoise라는 메서드도 정의합니다. 이 메서드는 기본 Vehicle 인스턴스에 대해 실제로 아무 것도 하지 않지만 나중에 Vehicle의 하위 클래스에 의해 사용자 정의됩니다.
class Vehicle {
var currentSpeed = 0.0
var description: String {
return "traveling at \(currentSpeed) miles per hour"
}
func makeNoise() {
}
}
- 이니셜라이저 구문을 사용하여 Vehicle의 새 인스턴스를 생성합니다. 이 구문은 유형 이름 뒤에 빈 괄호가 오는 형식으로 작성됩니다.
let someVehicle = Vehicle()
- 새 Vehicle 인스턴스를 만든 후에는 description 프로퍼티에 액세스하여 차량의 현재 속도에 대한 사람이 읽을 수 있는 설명을 인쇄할 수 있습니다.
print("Vehicle: \(someVehicle.description)")
- Vehicle 클래스는 임의의 차량에 대한 공통 특성을 정의하지만 그 자체로는 많이 사용되지 않습니다. 더 유용하게 만들려면 좀 더 구체적인 종류의 차량을 설명하도록 수정해야 합니다.
📌 Subclassing
서브클래싱은 기존 클래스를 기반으로 새 클래스를 만드는 행위입니다.
- 하위 클래스는 기존 클래스의 특성을 상속하므로 이를 구체화할 수 있습니다.
- 서브클래스에 새로운 특성을 추가할 수도 있습니다.
- 서브클래스에 수퍼클래스가 있음을 나타내려면 콜론으로 구분하여 수퍼클래스 이름 앞에 서브클래스 이름을 작성합니다.
class SomeSubclass: SomeSuperclass {
}
- 다음 예제는 Vehicle의 슈퍼클래스와 함께 Bicycle이라는 서브클래스를 정의합니다.
class Bicycle: Vehicle {
var hasBasket = false
}
새 Bicycle 클래스는 currentSpeed 및 description 프로퍼티, makeNoise() 메서드와 같은 Vehicle의 모든 특성을 자동으로 얻습니다.
- 상속된 특성 외에도 Bicycle 클래스는 기본값이 false인 새로운 저장 프로퍼티인 hasBasket을 정의합니다(프로퍼티에 대해 Bool 유형을 유추함).
- 기본적으로 새로 만드는 모든 Bicycle 인스턴스에는 바구니가 없습니다.
- 해당 인스턴스가 생성된 후 특정 Bicycle 인스턴스에 대해 hasBasket 프로퍼티을 true로 설정할 수 있습니다.
let bicycle = Bicycle()
bicycle.hasBasket = true
- 또한 Bicycle 인스턴스의 상속된 currentSpeed 프로퍼티을 수정하고 인스턴스의 상속된 설명 프로퍼티을 쿼리할 수도 있습니다.
bicycle.currentSpeed = 15.0
print("Bicycle: \(bicycle.description)")
- 하위 클래스는 자체적으로 하위 클래스가 될 수 있습니다. 다음 예제는 "탠덤"으로 알려진 2인승 자전거에 대한 Bicycle의 하위 클래스를 만듭니다.
class Tandem: Bicycle {
var currentNumberOfPassengers = 0
}
- Tandem은 Bicycle에서 모든 프로퍼티과 메서드를 상속하고, 차례로 Vehicle에서 모든 프로퍼티과 메서드를 상속합니다.
- 또한 Tandem 하위 클래스는 기본값이 0인 currentNumberOfPassengers라는 새 저장 프로퍼티을 추가합니다.
- Tandem의 인스턴스를 생성하는 경우 새 프로퍼티과 상속된 프로퍼티으로 작업하고 Vehicle에서 상속한 읽기 전용 설명 프로퍼티을 호출할 수 있습니다.
let tandem = Tandem()
tandem.hasBasket = true
tandem.currentNumberOfPassengers = 2
tandem.currentSpeed = 22.0
print("Tandem: \(tandem.description)")
📌 Overriding
- 하위 클래스는 인스턴스 메소드, 유형 메소드, 인스턴스 프로퍼티, 유형 프로퍼티 또는 하위 첨자에 대한 고유한 사용자 정의 구현을 제공할 수 있습니다.
- 그렇지 않으면 상위 클래스에서 상속됩니다.
- 이를 재정의라고 합니다.
- 상속될 특성을 재정의하려면 재정의 정의에 override 키워드를 접두어로 붙입니다.
이렇게 하면 재정의를 제공할 의도가 있고 실수로 일치하는 정의를 제공하지 않았음을 분명히 알 수 있습니다.
- 실수로 재정의하면 예기치 않은 동작이 발생할 수 있으며 override 키워드가 없는 재정의는 코드를 컴파일할 때 오류로 진단됩니다.
- override 키워드는 또한 재정의하는 클래스의 상위 클래스(또는 상위 클래스 중 하나)에 재정의를 위해 제공한 것과 일치하는 선언이 있는지 확인하도록 Swift 컴파일러에 프롬프트를 표시합니다.
📍 Accessing Superclass Methods, Properties, and Subscripts
- 하위 클래스에 대한 메서드, 프로퍼티 또는 하위 첨자 재정의를 제공할 때 재정의의 일부로 기존 상위 클래스 구현을 사용하는 것이 때때로 유용합니다.
- 예를 들어, 기존 구현의 동작을 구체화하거나 기존의 상속된 변수에 수정된 값을 저장할 수 있습니다.
- 적절한 경우 슈퍼 접두사를 사용하여 메서드, 프로퍼티 또는 하위 첨자의 슈퍼클래스 버전에 액세스합니다.
- someMethod()라는 재정의된 메서드는 재정의 메서드 구현 내에서 super.someMethod()를 호출하여 someMethod()의 슈퍼클래스 버전을 호출할 수 있습니다.
- someProperty라는 재정의된 프로퍼티은 재정의 getter 또는 setter 구현 내에서 super.someProperty로 someProperty의 수퍼클래스 버전에 액세스할 수 있습니다.
- someIndex에 대한 재정의된 첨자는 재정의하는 첨자 구현 내에서 super[someIndex]와 동일한 첨자의 수퍼클래스 버전에 액세스할 수 있습니다.
📍 Overriding Methods
📍 Overriding Properties
- 상속된 인스턴스 또는 유형 프로퍼티을 재정의하여 해당 프로퍼티에 대한 고유한 사용자 지정 getter 및 setter를 제공하거나 재정의 프로퍼티이 기본 프로퍼티 값이 변경될 때 관찰할 수 있도록 프로퍼티 관찰자를 추가할 수 있습니다.
📌 Overriding Property Getters and Setters
- 상속된 프로퍼티이 소스에서 저장 또는 계산된 프로퍼티으로 구현되는지 여부에 관계없이 상속된 프로퍼티을 재정의하기 위해 사용자 지정 getter(및 적절한 경우 setter)를 제공할 수 있습니다.
- 상속된 프로퍼티의 저장 또는 계산된 특성은 하위 클래스에서 알 수 없습니다.
- 상속된 프로퍼티에 특정 이름과 유형이 있다는 것만 알고 있습니다.
- 재정의가 동일한 이름 및 유형을 가진 수퍼클래스 프로퍼티과 일치하는지 컴파일러가 확인할 수 있도록 항상 재정의하는 프로퍼티의 이름과 유형을 모두 명시해야 합니다.
- 상속된 읽기 전용 프로퍼티을 하위 클래스 프로퍼티 재정의에 getter와 setter를 모두 제공하여 읽기-쓰기 프로퍼티으로 표시할 수 있습니다.
- 그러나 상속된 읽기-쓰기 프로퍼티을 읽기 전용 프로퍼티으로 표시할 수는 없습니다.
프로퍼티 재정의의 일부로 setter를 제공하는 경우 해당 재정의에 대한 getter도 제공해야 합니다.
재정의하는 getter 내에서 상속된 프로퍼티의 값을 수정하지 않으려면 getter에서 super.someProperty를 반환하여 상속된 값을 간단히 전달할 수 있습니다.
여기서 someProperty는 재정의하는 프로퍼티의 이름입니다.
- 다음 예제에서는 Vehicle의 하위 클래스인 Car라는 새 클래스를 정의합니다.
- Car 클래스는 기본 정수 값이 1인 gear라는 새로운 저장 프로퍼티을 도입했습니다.
- Car 클래스는 또한 Vehicle에서 상속한 description 프로퍼티을 재정의하여 현재 기어를 포함하는 사용자 지정 설명을 제공합니다.
class Car: Vehicle {
var gear = 1
override var description: String {
return super.description + " in gear \(gear)"
}
}
- description 프로퍼티의 재정의는 Vehicle 클래스의 description 프로퍼티을 반환하는 super.description을 호출하여 시작됩니다.
- 그런 다음 Car 클래스의 설명 버전은 이 설명 끝에 몇 가지 추가 텍스트를 추가하여 현재 장비에 대한 정보를 제공합니다.
- Car 클래스의 인스턴스를 만들고 해당 gear 및 currentSpeed 프로퍼티을 설정하면 해당 description 프로퍼티이 Car 클래스 내에 정의된 맞춤형 설명을 반환하는 것을 볼 수 있습니다.
let car = Car()
car.currentSpeed = 25.0
car.gear = 3
print("Car: \(car.description)")
📌 Overriding Property Observers
let automatic = AutomaticCar()
automatic.currentSpeed = 35.0
print("AutomaticCar: \(automatic.description)")
📌 Preventing Overrides
- 메서드, 프로퍼티 또는 아래 첨자를 final로 표시하여 재정의되는 것을 방지할 수 있습니다.
- 메서드, 프로퍼티 또는 아래 첨자의 도입기 키워드(예: final var, final func, final class func, final subscript) 앞에 final 수정자를 작성하여 이를 수행합니다.
- 하위 클래스의 최종 메서드, 프로퍼티 또는 하위 스크립트를 재정의하려는 모든 시도는 컴파일 시간 오류로 보고됩니다.
- 확장의 클래스에 추가하는 메서드, 프로퍼티 또는 첨자는 확장의 정의 내에서 final로 표시될 수도 있습니다.
- 클래스 정의(최종 클래스)에서 class 키워드 앞에 final 한정자를 작성하여 전체 클래스를 final로 표시할 수 있습니다.
- 최종 클래스를 하위 클래스로 만들려는 모든 시도는 컴파일 시간 오류로 보고됩니다.