継承
サブクラスを使用して機能を追加またはオーバーライドします。
クラスは他のクラスからメソッド、プロパティ、およびその他の特性を継承できます。あるクラスが別のクラスから継承する場合、継承するクラスはサブクラスと呼ばれ、継承されるクラスはスーパークラスと呼ばれます。継承は、Swiftのクラスを他の型と区別する基本的な動作です。
Swiftのクラスは、スーパークラスに属するメソッド、プロパティ、およびサブスクリプトを呼び出してアクセスでき、これらのメソッド、プロパティ、およびサブスクリプトの独自のオーバーライドバージョンを提供して、その動作を洗練または変更できます。Swiftは、オーバーライドの定義が一致するスーパークラスの定義を持つことを確認することで、オーバーライドが正しいことを保証します。
クラスは、プロパティの値が変更されたときに通知されるように、継承されたプロパティにプロパティオブザーバを追加することもできます。プロパティオブザーバは、元々格納プロパティとして定義されていたか計算プロパティとして定義されていたかに関係なく、任意のプロパティに追加できます。
基本クラスの定義
他のクラスから継承しないクラスは、基本クラスと呼ばれます。
注: Swiftのクラスは、普遍的な基本クラスから継承しません。スーパークラスを指定せずに定義したクラスは、自動的に基本クラスとなり、その上に構築できます。
以下の例では、Vehicle
という基本クラスを定義しています。この基本クラスは、デフォルト値が0.0の格納プロパティcurrentSpeed
を定義しています(プロパティの型はDouble
と推測されます)。currentSpeed
プロパティの値は、読み取り専用の計算プロパティdescription
によって使用され、車両の説明を作成します。
Vehicle
基本クラスは、makeNoise
というメソッドも定義しています。このメソッドは、基本Vehicle
インスタンスでは実際には何もしませんが、後でVehicle
のサブクラスによってカスタマイズされます。
class Vehicle {
var currentSpeed = 0.0
var description: String {
return "時速\(currentSpeed)マイルで走行中"
}
func makeNoise() {
// 何もしない - 任意の車両は必ずしも音を出すわけではない
}
}
初期化子構文を使用して、新しいVehicle
インスタンスを作成します。これは、型名の後に空の括弧を付けて記述します。
let someVehicle = Vehicle()
新しいVehicle
インスタンスを作成した後、そのdescription
プロパティにアクセスして、車両の現在の速度の人間が読める説明を印刷できます。
print("Vehicle: \(someVehicle.description)")
// Vehicle: 時速0.0マイルで走行中
Vehicle
クラスは、任意の車両の共通の特性を定義しますが、それ自体ではあまり役に立ちません。より具体的な種類の車両を記述するために、それを洗練する必要があります。
サブクラス化
サブクラス化は、既存のクラスに基づいて新しいクラスを作成する行為です。サブクラスは、既存のクラスから特性を継承し、それを洗練することができます。また、サブクラスに新しい特性を追加することもできます。
サブクラスがスーパークラスを持つことを示すには、サブクラス名の前にスーパークラス名をコロンで区切って記述します。
class SomeSubclass: SomeSuperclass {
// サブクラスの定義がここに入ります
}
次の例では、Vehicle
のスーパークラスを持つBicycle
というサブクラスを定義しています。
class Bicycle: Vehicle {
var hasBasket = false
}
新しいBicycle
クラスは、自動的にVehicle
の特性をすべて取得します。例えば、currentSpeed
およびdescription
プロパティやmakeNoise()
メソッドなどです。
継承した特性に加えて、Bicycle
クラスはhasBasket
という新しい格納プロパティを定義しており、デフォルト値はfalse
です(プロパティの型はBool
と推測されます)。
デフォルトでは、作成した新しいBicycle
インスタンスにはバスケットがありません。インスタンスを作成した後、そのhasBasket
プロパティをtrue
に設定できます。
let bicycle = Bicycle()
bicycle.hasBasket = true
また、Bicycle
インスタンスの継承したcurrentSpeed
プロパティを変更し、インスタンスの継承したdescription
プロパティを照会することもできます。
bicycle.currentSpeed = 15.0
print("Bicycle: \(bicycle.description)")
// Bicycle: 時速15.0マイルで走行中
サブクラスはさらにサブクラス化することができます。次の例では、「タンデム」として知られる二人乗り自転車のためのBicycle
のサブクラスを作成しています。
class Tandem: Bicycle {
var currentNumberOfPassengers = 0
}
Tandem
は、Bicycle
からすべてのプロパティとメソッドを継承し、Bicycle
はVehicle
からすべてのプロパティとメソッドを継承します。Tandem
サブクラスは、currentNumberOfPassengers
という新しい格納プロパティも追加しており、デフォルト値は0です。
Tandem
のインスタンスを作成すると、その新しいプロパティおよび継承したプロパティのいずれかを操作し、Vehicle
から継承した読み取り専用のdescription
プロパティを照会できます。
let tandem = Tandem()
tandem.hasBasket = true
tandem.currentNumberOfPassengers = 2
tandem.currentSpeed = 22.0
print("Tandem: \(tandem.description)")
// Tandem: 時速22.0マイルで走行中
オーバーライド
サブクラスは、スーパークラスから継承するインスタンスメソッド、タイプメソッド、インスタンスプロパティ、タイププロパティ、またはサブスクリプトに対して独自のカスタム実装を提供できます。これをオーバーライドと呼びます。
継承される特性をオーバーライドするには、オーバーライド定義の前に override
キーワードを付けます。これにより、オーバーライドを提供する意図があることが明確になり、誤って一致する定義を提供していないことが確認されます。誤ってオーバーライドすると予期しない動作が発生する可能性があり、override
キーワードなしのオーバーライドはコードのコンパイル時にエラーとして診断されます。
override
キーワードは、オーバーライドするクラスのスーパークラス(またはその親のいずれか)が提供した宣言と一致することを確認するために、Swiftコンパイラにチェックを促します。このチェックにより、オーバーライド定義が正しいことが保証されます。
スーパークラスのメソッド、プロパティ、およびサブスクリプトへのアクセス
サブクラスのメソッド、プロパティ、またはサブスクリプトのオーバーライドを提供する場合、オーバーライドの一部として既存のスーパークラスの実装を使用することが有用な場合があります。たとえば、その既存の実装の動作を改良したり、既存の継承された変数に修正された値を格納したりすることができます。
これが適切な場合、super
プレフィックスを使用してスーパークラスバージョンのメソッド、プロパティ、またはサブスクリプトにアクセスします:
someMethod()
という名前のオーバーライドされたメソッドは、オーバーライドメソッドの実装内でsuper.someMethod()
を呼び出すことでスーパークラスバージョンのsomeMethod()
を呼び出すことができます。someProperty
という名前のオーバーライドされたプロパティは、オーバーライドされたゲッターまたはセッターの実装内でsuper.someProperty
としてスーパークラスバージョンのsomeProperty
にアクセスできます。someIndex
のオーバーライドされたサブスクリプトは、オーバーライドされたサブスクリプトの実装内で同じサブスクリプトのスーパークラスバージョンにsuper[someIndex]
としてアクセスできます。
メソッドのオーバーライド
サブクラス内でメソッドのカスタマイズまたは代替実装を提供するために、継承されたインスタンスまたはタイプメソッドをオーバーライドできます。
次の例は、Vehicle
の新しいサブクラス Train
を定義し、Train
が Vehicle
から継承する makeNoise()
メソッドをオーバーライドするものです:
class Train: Vehicle {
override func makeNoise() {
print("Choo Choo")
}
}
新しい Train
インスタンスを作成し、その makeNoise()
メソッドを呼び出すと、Train
サブクラスバージョンのメソッドが呼び出されることがわかります:
let train = Train()
train.makeNoise()
// "Choo Choo" と出力されます
プロパティのオーバーライド
継承されたインスタンスまたはタイププロパティをオーバーライドして、そのプロパティのカスタムゲッターおよびセッターを提供したり、プロパティオブザーバーを追加して、オーバーライドされたプロパティが基になるプロパティ値の変更を監視できるようにすることができます。
プロパティゲッターおよびセッターのオーバーライド
継承されたプロパティがソースでストアドプロパティとして実装されているか計算プロパティとして実装されているかに関係なく、カスタムゲッター(および適切な場合はセッター)を提供してオーバーライドできます。サブクラスは、継承されたプロパティが特定の名前と型を持つことしか知らないため、継承されたプロパティのストアドまたは計算の性質はサブクラスには知られません。オーバーライドするプロパティの名前と型の両方を常に指定して、オーバーライドが同じ名前と型を持つスーパークラスプロパティと一致することをコンパイラが確認できるようにする必要があります。
サブクラスプロパティのオーバーライドでゲッターとセッターの両方を提供することで、継承された読み取り専用プロパティを読み書き可能なプロパティとして提示できます。ただし、継承された読み書き可能なプロパティを読み取り専用プロパティとして提示することはできません。
注:プロパティオーバーライドの一部としてセッターを提供する場合、そのオーバーライドのためにゲッターも提供する必要があります。オーバーライドされたゲッター内で継承されたプロパティの値を変更したくない場合は、オーバーライドされたゲッターから
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
プロパティのオーバーライドは super.description
を呼び出すことから始まり、これは Vehicle
クラスの description
プロパティを返します。Car
クラスの description
バージョンは、この説明の最後にいくつかの追加テキストを追加して、現在のギアに関する情報を提供します。
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
プロパティオブザーバーのオーバーライド
プロパティオーバーライドを使用して、継承されたプロパティにプロパティオブザーバーを追加できます。これにより、継承されたプロパティの値が変更されたときに通知を受け取ることができます。このプロパティが元々どのように実装されていたかに関係なくです。プロパティオブザーバーの詳細については、プロパティオブザーバーを参照してください。
注:継承された定数ストアドプロパティまたは継承された読み取り専用計算プロパティにプロパティオブザーバーを追加することはできません。これらのプロパティの値は設定できないため、オーバーライドの一部として
willSet
またはdidSet
の実装を提供することは適切ではありません。
また、同じプロパティに対してオーバーライドされたセッターとオーバーライドされたプロパティオブザーバーの両方を提供することもできません。プロパティの値の変更を監視したい場合で、そのプロパティに対して既にカスタムセッターを提供している場合は、カスタムセッター内から値の変更を監視するだけで済みます。
次の例は、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
オーバーライドの防止
メソッド、プロパティ、またはサブスクリプトを final
としてマークすることで、オーバーライドを防ぐことができます。これを行うには、メソッド、プロパティ、またはサブスクリプトの導入キーワード(final var
、final func
、final class func
、および final subscript
など)の前に final
修飾子を記述します。
サブクラスで final
メソッド、プロパティ、またはサブスクリプトをオーバーライドしようとすると、コンパイル時エラーとして報告されます。拡張機能にクラスに追加するメソッド、プロパティ、またはサブスクリプトも、拡張機能の定義内で final
としてマークできます。詳細については、拡張機能を参照してください。
クラス全体を final
としてマークするには、クラス定義の class
キーワードの前に final
修飾子を記述します(final class
)。final
クラスをサブクラス化しようとすると、コンパイル時エラーとして報告されます。