본문 바로가기
프로그래밍/Swift

[Swift] Inheritance (상속)

by 별준 2021. 12. 30.

References

Contents

  • Defining a Base Class
  • Subclassing
  • Overriding
  • Preventing Overrides

클래스는 다른 클래스로부터 메소드(methods), 속성(propertites), 그리고 다른 특징들을 상속(inherit)받을 수 있습니다. 다른 클래스로부터 상속을 받을 때, 상속을 받는 클래스는 subclass라고 하고, 상속을 하는 클래스는 superclass라고 합니다. 

 

Swift의 클래스는 그 클래스의 superclass에 속하는 메소드, 속성, 서브스크립트를 호출하거나 액세스할 수 있고, 메소드, 속성, 서브스크립트의 오버라이딩된 버전을 제공하여 그들의 동작을 수정할 수 있습니다. Swift는 오버라이드(override)가 superclass의 정의와 매칭되는 오버라이드 정의인지 체크함으로써 올바른지 확인할 수 있도록 도와줍니다.

 

클래스는 또한 상속된 속성에 옵저버 속성(property observers)를 추가하여 속성 값이 변경될 때 알림을 받을 수도 있습니다. 옵저버 속성은 저장 속성, 연산 속성으로 정의되었는지 여부와 관계없이 모든 속성에 추가할 수 있습니다.

 


Defining a Base Class

다른 클래스로부터 상속받지 않은 클래스를 base class라고 합니다.

 

아래 예제 코드는 Vehicle이라는 base class를 정의합니다. 이 base class는 currentSpeed라는 저장 속성을 정의하고 기본값은 0.0입니다. 따라서 Double로 타입이 추론됩니다. currentSpeed 속성의 값은 description이라는 read-only 연산 String 속성에 의해서 사용되며, 이는 vehicle의 설명을 생성합니다.

 

Vehicle base 클래스는 또한 makeNoise라는 메소드를 정의하는데, 이 메소드는 Vehicle 인스턴스에서는 아무것도 하지 않습니다. 이 메소드는 뒤에서 Vehicle의 subclass에서 커스터마이즈될 것입니다.

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()

새로운 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을 수퍼클래스로 하는 Bicycle 서브클래스를 정의합니다.

class Bicycle: Vehicle {
    var hasBasket = false
}

새로운 Bicycle 클래스는 자동으로 Vehicle의 모든 특성(currentSpeed와 description 속성과 makeNoise() 메소드)을 얻습니다. 상속받는 특성 이외에 Bicycle 클래스는 새로운 저장 속성인 hasBasket을 정의하고 기본값은 false로 설정합니다.

 

기본적으로 새로 생성되는 Bicycle 인스턴스의 hasBasket은 false입니다. 인스턴스가 생성된 후에 특정 Bicycle 인스턴스의 hasBasket 속성을 true로 설정할 수 있습니다.

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을 Bicycle의 서브클래스로 생성하는 예제 코드입니다.

class Tandem: Bicycle {
    var currentNumberOfPassengers = 0
}

Tandem은 Bicycle로부터 모든 속성과 메소드를 상속받으며, Bicycle은 Vehicle로부터 모든 속성과 메소드를 상속받습니다. 위 코드에서 Tandem 서브클래스는 currentNumberOfPassengers라는 새로운 저장 속성을 추가하고 기본값은 0으로 설정합니다.

 

만약 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 키워드가 없는 오버라이딩은 컴파일 시에 에러를 발생시킵니다.

 

또한, override 키워드는 Swift 컴파일러에게 오버라이딩 클래스의 수퍼클래스가 오버라이드를 통해 제공하는 것과 일치되는 정의를 가지고 있는지 체크하도록 합니다. 이는 오버라이딩 정의가 올바른지 확인합니다.

 

Accessing Superclass Methods, Properties, and Subscripts

서브클래스에 오버라이드 메소드, 속성, 서브스크립트를 제공할 때, 오버라이딩 구현 일부분에서 기존 수퍼클래스의 구현을 사용하는 것이 유용할 수 있습니다. 예를 들어, 기존의 구현의 동작을 구체화하거나 수정된 값을 기존의 상속된 변수에 저장할 수 있습니다.

 

수퍼클래스 버전의 메소드, 속성, 또는 서브스크립트는 super를 붙여서 액세스할 수 있습니다.

  • someMethod()라는 이름의 오버라이딩된 메소드는 오버라이딩 메소드 구현 내부에서 super.someMethod()를 호출하여 수퍼클래스의 someMethod()를 호출할 수 있습니다.
  • someProperty라는 이름의 오버라이딩된 속성은 오버라이딩된 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

상속받은 인스턴스 속성(instance property) 또는 타입 속성(type property)를 오버라이드하여 그 속성의 커스텀 getter와 setter를 제공하거나, 속성의 값이 변할 때를 관찰하는 옵저버 속성(property observer)를 추가할 수도 있습니다.

 

- Overriding Property Getters and Setters

서브클래스에서는 소스에서 저장 또는 연산 속성으로 구현되었는지 여부에 관계없이 어떠한 상속된 속성을 오버라이드하여 커스텀 getter/setter를 제공할 수 있습니다. 상속된 속성이 저장 속성인지 연산 속성인지는 서브클래스에서 알 수는 없으며, 오직 상속된 속성의 이름과 타입만 알 수 있습니다. 반드시 오버라이딩하려는 속성의 이름과 타입은 항상 명시해야합니다. (컴파일러가 오버라이드된 것이 수퍼클래스의 속성과 이름과 타입으로 일치하는지 확인하기 때문)

 

또한 상속받은 속성이 read-only라면 getter와 setter를 모두 제공해 read-write 속성으로 오버라이딩할 수 있습니다. 그러나 반대의 경우는 불가능합니다.

 

아래 예제 코드는 Vehicle의 서브클래스인 Car 클래스를 정의하고 있습니다. Car 클래스는 새로운 저장 속성 gear를 정의하고 기본값을 1로 설정합니다. 또한, Car 클래스는 Vehicle로부터 상속받은 description 속성을 오버라이드하여 현재 기어 정보와 함께 커스텀 description을 제공합니다.

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

description 속성의 오버라이드는 먼저 super.description을 호출하고, Vehicle 클래스의 description 속성을 반환받습니다. 그리고 Car 클래스의 descirption은 현재 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

상속받은 속성에 옵저버 속성(property observers)를 추가하여 오버라이딩할 수 있습니다. 이는 상속받은 속성의 값이 변경될 때 알리는 역할을 합니다.

옵저버 속성에 대한 내용은 아래 포스팅을 참조해주세요.

[Swift] Properties

 

[Swift] Properties

References https://docs.swift.org/swift-book/LanguageGuide/Properties.html Contents Stored Properties Computed Properties Property Observers Property Wrappers Global and Local Variables Type Propert..

junstar92.tistory.com

상속된 상수 저장 속성이나 read-only 연산 속성에는 옵저버 속성을 추가할 수 없습니다. 이들 속성의 값은 설정될 수 없고, 오버라이드 일부로 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으로 나누고 반올림을 하여 기어를 선택합니다. 속도가 35.0이라면 기어는 4가 됩니다.

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 modifier를 메소드, 속성, 서브스크립트 키워드 앞에 위치시키기만 하면 됩니다.

(ex, final var, final func, final class, final subscript)

 

서브클래스에서 final 메소드, 속성, 서브스크립트를 오버라이딩하려는 시도는 컴파일 에러를 일으킵니다. 

클래스를 정의할 때, class 키워드 앞에 final을 붙여서 전체 클래스를 final로 표시할 수 있습니다. 만약 final class를 서브클래스로 사용하려는 시도는 컴파일 에러를 일으킵니다.

'프로그래밍 > Swift' 카테고리의 다른 글

[Swift] Deinitialization  (0) 2022.01.06
[Swift] Initialization  (0) 2022.01.03
[Swift] Subscripts (서브스크립트)  (0) 2021.12.30
[Swift] Methods  (0) 2021.12.26
[Swift] Properties  (0) 2021.12.25

댓글