[최종 수정일 : 2018.08.29]

원문 : 애플 개발 문서 Swift 4.2 Language Guide - Inheritance

상속(Inheritance)

클래스는 다른 클래스로부터 메소드, 프로퍼티, 다른 특성등을 상속(inherit) 받을수 있습니다. 하나의 클래스가 다른 클래스로부터 상속받을때, 상속받는 클래스를 하위클래스(subclass), 상속하는 클래스를 상위클래스(superclass)라고 합니다. Swift에서 상속은 다른 타입으로 부터 클래스를 차별화하는 기본적인 동작입니다.

Swift에서 클래스는 상위클래스에 소속된(belonging) 메소드, 프로퍼티, 서브스크립트를 호출하고 사용할 수 있고, 이를 변경하거나 동작을 수정하기 위해 이러한 메소드, 프로퍼티, 서브스크립티의 오버라이딩(overriding)버젼을 제공할 수 있습니다. Swift는 오버라이드(override)정의가 상위클래스(superclass) 정의와 일치하는지 검사해서, 오버라이드(override)가 올바른지(correct) 확인하는 것을 도와줍니다.

클래스는 프로퍼티의 값이 변경될때 통지받기 위해 상속받은 프로퍼티에 프로퍼티 옵져버(property observers)를 추가할 수 있습니다. 프로퍼티 옵져버는 원래 정의된 저장 프로퍼티(stored property)나 계산 프로퍼티(computed property) 인지와 상관없이 모든 프로퍼티에 추가될수 있습니다.

기본 클래스 정의하기(Defining a Base Class)

다른 클래스로부터 상속받지 않는 모든 클래스를 기본 클래스(base class)라고 합니다.

주의
Swift 클래스는 보편적인(universal) 기본클래스로 부터 상속받지 않습니다. 여러분이 만드는 클래스에 상위클래스를 지정하지 않고 정의하면 자동적으로 기본 클래스가 됩니다.

아래 에제는 Vehicle 기본 클래스를 정의합니다. 기본 클래스는 저장 프로퍼티 currentSpeed를 기본값 0.0(프로퍼티 타입은 Double로 추론)으로 정의합니다. currentSpeed 프로퍼티의 값은 탈것(vehicle)의 설명을 만들기 위해 String 타입의 읽기전용(read-only) 계산 프로퍼티 descripton으로 사용됩니다.

기본 클래스 Vehicle는 makeNoise 메소드를 정의합니다. 이 메소드는 기본 Vehicle 인스턴스에 대해 실제 아무것도 하지않지만, 나중에 Vehicle의 하위클래스에 의해 커스터마이징(customized) 될수 있습니다.

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
    }
}

타입 이름 뒤에 빈 괄호를 작성하는 초기화 문법(initializer syntax)으로 Vehicle의 새로운 인스턴스를 생성합니다.

let someVehicle = Vehicle()

새로 생성된 Vehicle 인스턴스를 가지고, 탈것(vehicle)의 현재 속도에 대해 사람이 읽을수 있는 설명을 출력하기 위해 description 프로퍼티를 사용할 수 있습니다.

print("Vehicle: \(someVehicle.description)")
// Vehicle: traveling at 0.0 miles per hour

Vehicle 클래스는 임의(arbitrary)의 탈것에 대한 공통 특성을 정의하지만, 자체적으로 많이 사용하지는 않습니다. 더 유용하게 만들기 위해, 탈것의 구체적인 종류를 설명하기 위해 수정(refine)이 필요합니다.

서브클래싱(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(프로퍼티는 Bool타입으로 추론)로 정의합니다.

기본적으로, 모든 새로운 Bicycle 인스턴스는 바구니를 가지지 않고 만들 것입니다. 특정 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

하위클래스는 스스로가 하위클래스가 될수 있습니다. 다음 예제는 tandem이라는 2인승 자전거(two-seater bicycle)에 대한 Bicycle의 하위클래스를 생성합니다.

class Tandem: Bicycle {
    var currentNumberOfPassengers = 0
}

Tendem은 Vehicle으로부터 모든 프로퍼티와 메소드를 차례대로 상속 받은 Bicycle으로부터 모든 프로퍼티와 메소드를 상속받았습니다. Tandem 하위클래스는 새 저장 프로퍼티 currentNumberOfPassengers를 기본 값 0으로 추가하였습니다.

Tandem의 인스턴스를 생성하는 경우, 새로 작성하고 상속받은 프로퍼티 모두를 사용할 수 있고, Vehicle으로 부터 상속받은 읽기 전용 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 키워드 없는 모든 오버라이딩은 코드가 컴파일될때 오류로 진단됩니다.

override 키둬드는 오버라이딩하는 클래스의 상위클래스가 오버라이딩을 제공하는 정의가 일치하는지 검사를 위해 Swift 컴파일러에게 묻습니다(prompts) . 이 검사는 오버라이딩 정의가 올바른지 확인합니다.

상위클래스 메소드, 프로퍼티, 서브스크립트 사용하기(Accessing Superclass Methods, Properties, and Subscripts)

하위클래스에 대한 메소드, 프로퍼티, 서브스크립트 오버라이딩을 제공할때, 기존 상위클래스 구현을 오버라이딩에서 사용하는 것이 유용할 때가 있습니다. 예를들어, 기존 구현의 동작을 수정하거나, 기존에 상속받은 변수에 수정된 값을 저장할 수 있습니다.

이것이 적절한 경우에, super 접두사를 사용해서, 상위클래스 버젼의 메소드, 프로퍼티, 서브스크립트를 사용합니다:

  • 오버라이드된 메소드 someMethod()은 오버라이딩을 구현하면서, super.someMethod()를 호출해서 상위클래스 버젼의 someMethod()를 호출할 수 있습니다.
  • 오버라이드된 프로퍼티 someProperty는 getter이나 setter의 오버라이딩을 구현하면서, super.someProperty 처럼 상위클래스의 someProperty를 사용할 수 있습니다.
  • someIndex에 대해 오버라이드된 서브스크립트는 오버라이딩을 구현하면서 super[someIndex]처럼 상위 클래스 버젼의 동일한 서브스크립트를 사용할 수 있습니다.

메소드 오버라이딩하기(Overriding Method)

하위클래스에서 맞춤(tailoredor) 또는 대체(alternative) 메소드의 구현을 위해, 상속받은 인스턴스나 타입 메소드를 오버라이딩 할수 있습니다.

다음 예제는 Vehicle으로부터 상속받은 Train이 makeNoise() 메소드를 오버라이딩하는 Vehicle의 새로운 하위클래스 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을 커스터마이징해서 제공하기 위해 상속받은 인스턴스 또는 타입 프로퍼티를 오버라이딩 할수 있으며, 프로퍼티 기본 값이 변경될때 오버라이딩하는 프로퍼티를 감지(overse)가 가능하도록 프로퍼티 옵져버를 추가할수 있습니다.

프로퍼티의 Getter와 Setter를 오버라이딩하기(Overriding Property Getters and Setters)

상속받은 모든(any) 프로퍼티를 오버라이드 하는 경우에, 사용자정의 getter를 제공할수 있으며, 상속받은 프로퍼티 인지와 관계없이 소스에 저장 프로퍼티나 계산 프로퍼티로 구현됩니다. 하위클래스는 상속받은 프로퍼티가 저장 프로퍼티인지 게산 프로퍼티인지 알수가 없습니다. - 상속받은 프로퍼티가 어떤 이름과 타입인지만 알고 있습니다. 항상 오버라이딩하려는 프로퍼티의 이름과 타입을 확인해야하며, 컴파일러가 오버라이드하는 것과 같은 이름과 타입인 상위클래스 프로퍼티인지 검사하도록 합니다.

상속받은 읽기전용(read-only) 프로퍼티를 하위클래스 프로퍼티 오버라이딩에서 getter와 setter 모드를 제공해서 읽기-쓰기(read-write) 프로퍼티로 표시할 수 있습니다. 하지만, 상속받은 읽기-쓰기(read-write) 프로퍼티를 읽기전용(read-only)프로퍼티로 표시할 수는 없습니다.

주의
프로퍼티 오버라이딩에서 setter을 제공하는 경우에, 그 오버라이딩에서 getter 또한 제공해야 합니다. 오버라이딩하는 getter를 사용하여 상속받은 프로퍼티의 값을 수정하지 않길 원하는 경우에, getter에서 super.someProperty를 반환해서 단순히 상속받은 값을 전달할 수 있으며, someProperty는 오버라이딩 하는 프로퍼티의 이름입니다.

다음 예제는 Vehicle의 하위클래스인 새로운 클래스 Car를 정의합니다. Car클래스는 기본 정수형 값 1인 새로운 저장 프로퍼티 gear를 도입합니다. Car클래스는 현재 gear에 관한 설명을 제공하기 위해 Vehicle으로 상속받은 description 프로퍼티를 오버라이딩 합니다.

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

description프로퍼티의 오버라이딩은 Vehicle 클래스의 description 프로퍼티를 반환하는 super.description을 호출하는 것으로 시작합니다. Car클래스의 description의 버젼은 현재 gear에 관한 정보를 제공하기 위해 설명 끝부분에 추가적인 텍스트를 추가합니다.

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

프로퍼티 옵져버를 오버라이딩하기(Overriding Property Observers)

상속받은 프로퍼티에 프로퍼티 옵져버를 추가하기 위해 프로퍼티 오버라이딩을 사용할 수 있습니다. 이것은 프로퍼티가 원래 구현된 것과 상관없이, 상속받은 프로퍼티의 값이 변경될때 알려주는 것이 가능합니다. 프로퍼티 옵져버에 대한 자세한 정보는, 프로퍼티 옵져버(Property Observers)를 보세요.

주의
상속받은 상수 저장 프로퍼티나 상속받은 읽기전용 계산 프로퍼티에 프로퍼티 옵져버를 추가할 수 없습니다. 이러한 프로퍼티의 값은 설정할수 없고, 오버라이딩에서 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.0의 속도는 기어 4가 됩니다.

let automatic = AutomaticCar()
automatic.currentSpeed = 35.0
print("AutomaticCar: \(automatic.description)")
// AutomaticCar: traveling at 35.0 miles per hour in gear 4

오버라이딩 막기(Perventing Overrides)

final로 표시하면 메소드, 프로퍼티, 서브스크립트를 오버라이딩하는 것을 막을 수 있습니다. 메소드, 프로퍼티, 서브스크립트를 나타내는 키워드(final var, final func, final class func, final subscript) 앞에 final 수식어를 작성합니다.

하위클래스에서 final 메소드, 프로퍼티, 서브스크립트를 오버라이딩 하려고 하면 컴파일시에 오류가 발생합니다. 메소드, 프로퍼티, 서브스크립트를 클래스에 추가하는 확장(extension)의 정의에서 final로 표시 할 수 있습니다.

클래스 정의에서 class 키워드 앞에 final 수정자를 작성해서 클래스 전체가 final로 표시되게 할수 있습니다(final classs). final 클래스의 하위클래스를 만드려고 하면 컴파일 오류가 발생합니다.

'Swift > Language Guide' 카테고리의 다른 글

Deinitialization  (0) 2018.09.18
Initialization  (0) 2018.09.17
Inheritance  (0) 2018.08.30
Subscripts  (0) 2018.08.30
Methods  (0) 2018.08.30
Properties  (0) 2018.08.30
Posted by 까칠코더