티스토리 뷰

반응형

이번 글에서는 Swift 공식 문서의 13번째 단원인 Inheritance를 읽고 정리한 글을 쓰려고 한다.

 

Apple Swift 공식문서 13단원 - Inheritance

 

Inheritance

클래스는 다른 클래스에게 메서드, 프로퍼티, 그리고 다른 것들을 상속해줄 수 있다. 상속을 해주는 클래스를 superclass(슈퍼클래스), 상속을 받는 클래스를 subclass(서브클래스)라고 한다. 상속은 Swift에서 클래스를 차별화해주는 기능이라고 볼 수 있다.

Swift에서의 서브클래스는 슈퍼클래스의 메서드, 프로퍼티, 서브스크립트에 접근할 수 있고 호출할 수 있다. 또한 슈퍼클래스의 메서드, 프로퍼티, 서브 스크립트를 오버 라이딩하여 서브클래스 자체의 것도 만들 수 있다. Swift는 서브클래스에서 오버 라이딩으로 정의한 것들이 슈퍼클래스에 정의되어있는지 확인하여 오류를 발견해준다.

클래스는 프로퍼티 옵저버를 추가하여 상속된 프로퍼티의 값이 변할 때를 인지할 수도 있다. 프로퍼티 옵서버는 저장 프로퍼티인지 계산 프로퍼티인지 관계없이 어떠한 프로퍼티에도 추가될 수 있다.


Defining a Base Class

다른 클래스에 상속받지 않은 클래스를 Base Class(베이스 클래스)라고 한다. Swift에서 정의한 클래스는 universal base class에 상속되지 않는다. 즉 슈퍼 클래스를 지정하지 않고 정의한 클래스는 자동적으로 베이스 클래스가 되게 된다.

그럼 베이스 클래스의 예를 보자

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
    }
}
let someVehicle = Vehicle()
print("Vehicle: \(someVehicle.description)")
// Vehicle: traveling at 0.0 miles per hour

위의 코드가 베이스 클래스를 정의한 예이다. 사실 그냥 일반 클래스를 정의하고 상속을 사용하지 않으면 그것이 베이스 클래스이다. 이러한 클래스의 인스턴스를 생성하고 프로퍼티에 접근하는 것도 이전의 Structures and Classes단원에서 본 방법과 동일하다.


Subclassing

그럼 이번엔 서브클래스를 만드는 방법을 알아보자. 어떠한 클래스가 상속을 받았다는 것을 정의하기 위해선 다음과 같이 서브클래스의 이름 뒤에 콜론(:)을 쓰고 뒤에 상속받을 슈퍼클래스의 이름을 쓰면 된다.

class SomeSubclass: SomeSuperclass {
    // subclass definition goes here
}

그럼 이러한 방법으로 아까 정의한 Vechicle 클래스를 상속받는 Bicycle 클래스를 정의해보자.

class Bicycle: Vehicle {
    var hasBasket = false
}

이렇게 상속을 받게 되면 실제로 Bicycle클래스의 프로퍼티는 hasBasket이라는 Bool타입밖에 없는 것 같지만 아까 Vehicle 클래스에서 정의한 프로퍼티와 메서드를 모두 상속받게 된다. 즉 다음과 같은 사용이 가능하다는 것이다.

bicycle.currentSpeed = 15.0
print("Bicycle: \(bicycle.description)")
// Bicycle: traveling at 15.0 miles per hour

이렇게 상속받은 프로퍼티에 접근할 수 있다. 서브클래스들은 또 다른 클래스의 슈퍼클래스가 될 수 있는데 이번에는 Bicycle클래스를 상속받는 Tandem 클래스를 정의해보자

class Tandem: Bicycle {
    var currentNumberOfPassengers = 0
}

이렇게 되면 Tandem클래스는 Bicycle클래스를 상속받았고 Bicycle클래스는 Vehicle클래스를 상속받았다. 즉 Tandem은 자동적으로 Vehicle클래스도 상속받게 된 것이다. 즉 다음과 같이 Vehicle의 프로퍼티에도 접근할 수 있다.

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 키워드를 사용하지 않으면 오류를 발생하게 된다.

 

Accessing Superclass Methods, Properties, and Subscripts

 

서브클래스에서 오버라이드를 사용한다면 슈퍼클래스에 이미 존재하는 구현의 일부를 사용하는 것이 유용한 경우가 있다. 예를 들어 기존 구현의 동작을 세분화하거나 상속된 변수에 수정된 값을 저장하는 경우가 있다. 만약 서브 클래스에서 오버라이드를 사용해 기존 구현을 다시 만들었더라도 슈퍼 클래스의 것들을 사용할 수 있다. 이때 super를 사용하여 슈퍼클래스의 메서드, 프로퍼티 등에 접근할 수 있다.

 

Overriding Methods

 

상속받은 인스턴스, 타입 메서드를 오버라이드 하여 서브 클래스에서 자체적인 메서드를 정의 할 수 있다.

class Train: Vehicle {
    override func makeNoise() {
        print("Choo Choo")
    }
}
let train = Train()
train.makeNoise()
// Prints "Choo Choo"

위의 코드와 같이 Vehicle 클래스를 상속받은 Train 클래스에서 상속받은 makeNoise() 메서드를 오버라이드하여 자체 메서드로 정의할 수 있다.

 

Overriding Properties

 

상속받은 인스턴스나 타입 프로퍼티도 오버라이드 할 수 있다. 이때 getter, setter도 만들 수 있고 오버라이드 한 프로퍼티가 원래의 프로퍼티를 변경하는 것을 감지할 수도 있다.

 

Overriding Property Getters and Setters

 

계산 프로퍼티든 저장 프로퍼티든 상속된 프로퍼티를 오버라이드 한 프로퍼티는 getter, setter를 가질 수 있다. 이렇게 상속받은 프로퍼티가 저장 프로퍼티인지 계산 프로퍼티인지는 오버라이드 할 때 직접 타입을 정의해줘야한다. 슈퍼클래스에서 read-write로 선언된 프로퍼티를 서브클래스에서 read only로 오버라이드할 순 없다. 하지만 슈퍼클래스에서 read only로 선언된 프로퍼티를 read-write로 오버라이드 할 순 있다. 그럼 프로퍼티를 오버라이드하는 예를 보자.

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

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

위의 코드에서 description 프로퍼티를 오버라이드 한 것을 볼 수 있다. 반환값에 super로 접근한 description은 슈퍼클래스의 프로퍼티이다. 즉 슈퍼클래스의 프로퍼티에 in gear (gear)을 더한 문자열을 서브 클래스의 description으로 정의한다는 것이다. 실제 인스턴스를 생성하여 메서드를 호출해보면 서브클래스에서 오버라이드한 메서드가 호출되는 것을 볼 수 있다.

 

Overriding Property Observers

 

상속된 프로퍼티엔 오버라이드를 사용하여 프로퍼티 옵서버도 추가할 수 있다. 이러한 옵서버는 슈퍼 클래스의 프로퍼티가 원래 구현된 방식에 관계없이 변경되는 것을 감지해준다. 상속된 상수 저장 프로퍼티나 상속된 read-only 계산 프로퍼티에는 프로퍼티 옵서버를 추가할 수 없다. 이러한 프로퍼티들은 set을 할 수 없기 때문에 willSet, didSet도 사용할 수 없다. 오버라이딩 setter와 오버라이딩 프로퍼티 옵저버를 같은 프로퍼티에 정의할 순 없다. 프로퍼티 값의 변화를 관찰하고 싶은데 오버라이딩 setter를 이미 만들었다면 setter 내에서 이를 간단하게 관찰 할 수 있다.

그럼 이러한 프로퍼티 옵저버를 오버라이드 하는 예를 보자.

class AutomaticCar: Car {
    override var currentSpeed: Double {
        didSet {
            gear = Int(currentSpeed / 10.0) + 1
        }
    }
}

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

위의 코드는 Car의 서브클래스인 AutomaticCar 클래스를 정의한 코드이다. 또한 여기에는 currentSpeed 프로퍼티를 오버라이드 했으며 이 프로퍼티에는 프로퍼티 옵서버가 정의되어있다. 인스턴스를 생성하여 currentSpeed 프로퍼티의 값을 변경해주면 프로퍼티 옵서버에 의해 gear 프로퍼티의 값이 변경되게 된다.


Preventing Overrides

 

final 키워드를 사용하여 메서드, 프로퍼티, 타입 프로퍼티가 오버라이드가 되는 것을 방지할 수 있다. 메서드, 프로퍼티, 타입 프로퍼티의 키워드 앞에 final을 작성하게 되면 오버라이드를 할 수 없게 된다. (final var, final func, final class func, final subscript) 이렇게 정의한 것들을 오버라이드 하려고 시도하면 컴파일 에러를 발생하게 된다. 이는 클래스를 정의할 때도 가능한데 클래스 키워드 앞에 final을 작성하게 되면 해당 클래스를 슈퍼클래스로 하는 서브클래스를 만들 수 없게 된다.

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함