Inheritance Swift


Inheritance

클래스는 메소드, 속성, 다른 특성을 다른 클래스로부터 상속받을 수 있다. 한 클래스가 다른 클래스로부터 상속을 받으면 상속받는 클래스는 서브클래스라하고 상속하는 클래스는 슈퍼클래스라 한다. 상속성은 스위프트에서 다른 형식과 구별되는 클래스의 기능이다.

스위프트에서 클래스는 메소드, 프로퍼티, 그리고 그 슈퍼클래스에 연관되는 서브스크립트를 호출하고 접근할 수 있으며 이들 메소드, 프로퍼티, 서브스크립트를 재정의하거나 자체적인 작용으로 수정할 수 있다. Swift helps to ensure your overrides are correct by checking that the override definition has a matching superclass definition.

Classes can also add property observers to inherited properties in order to be notified when the value of a property changes. Property observers can be added to any property, regardless of whether it was originally defined as a stored or computed property.

기반 클래스 정의하기

Any class that does not inherit from another class is known as a base class.

NOTE

Swift classes do not inherit from a universal base class. Classes you define without specifying a superclass automatically become base classes for you to build upon.

The example below defines a base class called Vehicle. This base class defines a stored property called currentSpeed, with a default value of 0.0 (inferring a property type of Double). The currentSpeed property’s value is used by a read-only computed String property called description to create a description of the vehicle.

The Vehicle base class also defines a method called makeNoise. This method does not actually do anything for a base Vehicle instance, but will be customized by subclasses of Vehicle later on:

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

You create a new instance of Vehicle with initializer syntax, which is written as a type name followed by empty parentheses:

let someVehicle = Vehicle()
Having created a new Vehicle instance, you can access its description property to print a human-readable description of the vehicle’s current speed:

print("Vehicle: \(someVehicle.description)")
// Vehicle: traveling at 0.0 miles per hour
The Vehicle class defines common characteristics for an arbitrary vehicle, but is not much use in itself. To make it more useful, you need to refine it to describe more specific kinds of vehicles.

서브클래싱

Subclassing is the act of basing a new class on an existing class. The subclass inherits characteristics from the existing class, which you can then refine. You can also add new characteristics to the subclass.

To indicate that a subclass has a superclass, write the subclass name before the superclass name, separated by a colon:

class SomeSubclass: SomeSuperclass {
    // subclass definition goes here
}
The following example defines a subclass called Bicycle, with a superclass of Vehicle:

class Bicycle: Vehicle {
    var hasBasket = false
}
The new Bicycle class automatically gains all of the characteristics of Vehicle, such as its currentSpeed and description properties and its makeNoise() method.

In addition to the characteristics it inherits, the Bicycle class defines a new stored property, hasBasket, with a default value of false (inferring a type of Bool for the property).

By default, any new Bicycle instance you create will not have a basket. You can set the hasBasket property to true for a particular Bicycle instance after that instance is created:

let bicycle = Bicycle()
bicycle.hasBasket = true
You can also modify the inherited currentSpeed property of a Bicycle instance, and query the instance’s inherited description property:

bicycle.currentSpeed = 15.0
print("Bicycle: \(bicycle.description)")
// Bicycle: traveling at 15.0 miles per hour
Subclasses can themselves be subclassed. The next example creates a subclass of Bicycle for a two-seater bicycle known as a “tandem”:

class Tandem: Bicycle {
    var currentNumberOfPassengers = 0
}
Tandem inherits all of the properties and methods from Bicycle, which in turn inherits all of the properties and methods from Vehicle. The Tandem subclass also adds a new stored property called currentNumberOfPassengers, with a default value of 0.

If you create an instance of Tandem, you can work with any of its new and inherited properties, and query the read-only description property it inherits from 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

오버라이딩

A subclass can provide its own custom implementation of an instance method, type method, instance property, type property, or subscript that it would otherwise inherit from a superclass. This is known as overriding.

To override a characteristic that would otherwise be inherited, you prefix your overriding definition with the override keyword. Doing so clarifies that you intend to provide an override and have not provided a matching definition by mistake. Overriding by accident can cause unexpected behavior, and any overrides without the override keyword are diagnosed as an error when your code is compiled.

The override keyword also prompts the Swift compiler to check that your overriding class’s superclass (or one of its parents) has a declaration that matches the one you provided for the override. This check ensures that your overriding definition is correct.

슈퍼클래스 메소드, 프로퍼티, 서브스크립트에 접근하기

When you provide a method, property, or subscript override for a subclass, it is sometimes useful to use the existing superclass implementation as part of your override. For example, you can refine the behavior of that existing implementation, or store a modified value in an existing inherited variable.

Where this is appropriate, you access the superclass version of a method, property, or subscript by using the super prefix:

An overridden method named someMethod() can call the superclass version of someMethod() by calling super.someMethod() within the overriding method implementation.
An overridden property called someProperty can access the superclass version of someProperty as super.someProperty within the overriding getter or setter implementation.
An overridden subscript for someIndex can access the superclass version of the same subscript as super[someIndex] from within the overriding subscript implementation.

오버라이딩 메소드

You can override an inherited instance or type method to provide a tailored or alternative implementation of the method within your subclass.

The following example defines a new subclass of Vehicle called Train, which overrides the makeNoise() method that Train inherits from Vehicle:

class Train: Vehicle {
    override func makeNoise() {
        print("Choo Choo")
    }
}
If you create a new instance of Train and call its makeNoise() method, you can see that the Train subclass version of the method is called:

let train = Train()
train.makeNoise()
// Prints "Choo Choo"

오버라이딩 프로퍼티

You can override an inherited instance or type property to provide your own custom getter and setter for that property, or to add property observers to enable the overriding property to observe when the underlying property value changes.


프로퍼티 게터와 세터 오버라이딩

You can provide a custom getter (and setter, if appropriate) to override any inherited property, regardless of whether the inherited property is implemented as a stored or computed property at source. The stored or computed nature of an inherited property is not known by a subclass—it only knows that the inherited property has a certain name and type. You must always state both the name and the type of the property you are overriding, to enable the compiler to check that your override matches a superclass property with the same name and type.

You can present an inherited read-only property as a read-write property by providing both a getter and a setter in your subclass property override. You cannot, however, present an inherited read-write property as a read-only property.

NOTE

If you provide a setter as part of a property override, you must also provide a getter for that override. If you don’t want to modify the inherited property’s value within the overriding getter, you can simply pass through the inherited value by returning super.someProperty from the getter, where someProperty is the name of the property you are overriding.

The following example defines a new class called Car, which is a subclass of Vehicle. The Car class introduces a new stored property called gear, with a default integer value of 1. The Car class also overrides the description property it inherits from Vehicle, to provide a custom description that includes the current gear:

class Car: Vehicle {
    var gear = 1
    override var description: String {
        return super.description + " in gear \(gear)"
    }
}
The override of the description property starts by calling super.description, which returns the Vehicle class’s description property. The Car class’s version of description then adds some extra text onto the end of this description to provide information about the current gear.

If you create an instance of the Car class and set its gear and currentSpeed properties, you can see that its description property returns the tailored description defined within the Car class:

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

프로퍼티 옵져버 오버라이딩

상속된 프로퍼티로 프로퍼티 옵저버를 추가하기 위해 프로퍼티 오버라이딩을 사용할 수 있다. 이 것은 프로퍼티가 어떻게 원본에서 구현되었는지 상관없이  상속된 프로퍼티 변화 발생시 알림을 받을 수 있게 한다. For more information on property observers, see Property Observers.

일러두기)
상속된 상수로 저장된 프로퍼티나 상속된 읽기전용 계산된 프로퍼티에는 프로퍼티 옵져버를 생성할 수 없다. 이들 프로퍼티의 값은 설정될 수 없으니 willSet  또는 didSet 을 오버라이드 일부로 제공하는 것은 부적절하다.

Note also that you cannot provide both an overriding setter and an overriding property observer for the same property. If you want to observe changes to a property’s value, and you are already providing a custom setter for that property, you can simply observe any value changes from within the custom setter.

다음 예시는 AutomaticCar 라는 새로운 클래스를 정의하는데 Car 의 서브클래스이다. AutomaticCar 는 자동 기어박스를 가진 Car 로서 현 속도에 기반하여 적절한 기어를 자동으로 선택한다.

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

Whenever you set the currentSpeed property of an AutomaticCar instance, the property’s didSet observer sets the instance’s gear property to an appropriate choice of gear for the new speed. Specifically, the property observer chooses a gear that is the new currentSpeed value divided by 10, rounded down to the nearest integer, plus 1. A speed of 35.0 produces a gear of 4:

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

오버라이드 방지하기

You can prevent a method, property, or subscript from being overridden by marking it as final. Do this by writing the final modifier before the method, property, or subscript’s introducer keyword (such as final var, final func, final class func, and final subscript).

Any attempt to override a final method, property, or subscript in a subclass is reported as a compile-time error. Methods, properties, or subscripts that you add to a class in an extension can also be marked as final within the extension’s definition.

You can mark an entire class as final by writing the final modifier before the class keyword in its class definition (final class). Any attempt to subclass a final class is reported as a compile-time error.


덧글

댓글 입력 영역