Skip to content

継承

サブクラスを使用して機能を追加またはオーバーライドします。

クラスは他のクラスからメソッド、プロパティ、およびその他の特性を継承できます。あるクラスが別のクラスから継承する場合、継承するクラスはサブクラスと呼ばれ、継承されるクラスはスーパークラスと呼ばれます。継承は、Swiftのクラスを他の型と区別する基本的な動作です。

Swiftのクラスは、スーパークラスに属するメソッド、プロパティ、およびサブスクリプトを呼び出してアクセスでき、これらのメソッド、プロパティ、およびサブスクリプトの独自のオーバーライドバージョンを提供して、その動作を洗練または変更できます。Swiftは、オーバーライドの定義が一致するスーパークラスの定義を持つことを確認することで、オーバーライドが正しいことを保証します。

クラスは、プロパティの値が変更されたときに通知されるように、継承されたプロパティにプロパティオブザーバを追加することもできます。プロパティオブザーバは、元々格納プロパティとして定義されていたか計算プロパティとして定義されていたかに関係なく、任意のプロパティに追加できます。

基本クラスの定義

他のクラスから継承しないクラスは、基本クラスと呼ばれます。

注: Swiftのクラスは、普遍的な基本クラスから継承しません。スーパークラスを指定せずに定義したクラスは、自動的に基本クラスとなり、その上に構築できます。

以下の例では、Vehicleという基本クラスを定義しています。この基本クラスは、デフォルト値が0.0の格納プロパティcurrentSpeedを定義しています(プロパティの型はDoubleと推測されます)。currentSpeedプロパティの値は、読み取り専用の計算プロパティdescriptionによって使用され、車両の説明を作成します。

Vehicle基本クラスは、makeNoiseというメソッドも定義しています。このメソッドは、基本Vehicleインスタンスでは実際には何もしませんが、後でVehicleのサブクラスによってカスタマイズされます。

swift
class Vehicle {
    var currentSpeed = 0.0
    var description: String {
        return "時速\(currentSpeed)マイルで走行中"
    }
    func makeNoise() {
        // 何もしない - 任意の車両は必ずしも音を出すわけではない
    }
}

初期化子構文を使用して、新しいVehicleインスタンスを作成します。これは、型名の後に空の括弧を付けて記述します。

swift
let someVehicle = Vehicle()

新しいVehicleインスタンスを作成した後、そのdescriptionプロパティにアクセスして、車両の現在の速度の人間が読める説明を印刷できます。

swift
print("Vehicle: \(someVehicle.description)")
// Vehicle: 時速0.0マイルで走行中

Vehicleクラスは、任意の車両の共通の特性を定義しますが、それ自体ではあまり役に立ちません。より具体的な種類の車両を記述するために、それを洗練する必要があります。

サブクラス化

サブクラス化は、既存のクラスに基づいて新しいクラスを作成する行為です。サブクラスは、既存のクラスから特性を継承し、それを洗練することができます。また、サブクラスに新しい特性を追加することもできます。

サブクラスがスーパークラスを持つことを示すには、サブクラス名の前にスーパークラス名をコロンで区切って記述します。

swift
class SomeSubclass: SomeSuperclass {
    // サブクラスの定義がここに入ります
}

次の例では、Vehicleのスーパークラスを持つBicycleというサブクラスを定義しています。

swift
class Bicycle: Vehicle {
    var hasBasket = false
}

新しいBicycleクラスは、自動的にVehicleの特性をすべて取得します。例えば、currentSpeedおよびdescriptionプロパティやmakeNoise()メソッドなどです。

継承した特性に加えて、BicycleクラスはhasBasketという新しい格納プロパティを定義しており、デフォルト値はfalseです(プロパティの型はBoolと推測されます)。

デフォルトでは、作成した新しいBicycleインスタンスにはバスケットがありません。インスタンスを作成した後、そのhasBasketプロパティをtrueに設定できます。

swift
let bicycle = Bicycle()
bicycle.hasBasket = true

また、Bicycleインスタンスの継承したcurrentSpeedプロパティを変更し、インスタンスの継承したdescriptionプロパティを照会することもできます。

swift
bicycle.currentSpeed = 15.0
print("Bicycle: \(bicycle.description)")
// Bicycle: 時速15.0マイルで走行中

サブクラスはさらにサブクラス化することができます。次の例では、「タンデム」として知られる二人乗り自転車のためのBicycleのサブクラスを作成しています。

swift
class Tandem: Bicycle {
    var currentNumberOfPassengers = 0
}

Tandemは、Bicycleからすべてのプロパティとメソッドを継承し、BicycleVehicleからすべてのプロパティとメソッドを継承します。Tandemサブクラスは、currentNumberOfPassengersという新しい格納プロパティも追加しており、デフォルト値は0です。

Tandemのインスタンスを作成すると、その新しいプロパティおよび継承したプロパティのいずれかを操作し、Vehicleから継承した読み取り専用のdescriptionプロパティを照会できます。

swift
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 を定義し、TrainVehicle から継承する makeNoise() メソッドをオーバーライドするものです:

swift
class Train: Vehicle {
    override func makeNoise() {
        print("Choo Choo")
    }
}

新しい Train インスタンスを作成し、その makeNoise() メソッドを呼び出すと、Train サブクラスバージョンのメソッドが呼び出されることがわかります:

swift
let train = Train()
train.makeNoise()
// "Choo Choo" と出力されます

プロパティのオーバーライド

継承されたインスタンスまたはタイププロパティをオーバーライドして、そのプロパティのカスタムゲッターおよびセッターを提供したり、プロパティオブザーバーを追加して、オーバーライドされたプロパティが基になるプロパティ値の変更を監視できるようにすることができます。

プロパティゲッターおよびセッターのオーバーライド

継承されたプロパティがソースでストアドプロパティとして実装されているか計算プロパティとして実装されているかに関係なく、カスタムゲッター(および適切な場合はセッター)を提供してオーバーライドできます。サブクラスは、継承されたプロパティが特定の名前と型を持つことしか知らないため、継承されたプロパティのストアドまたは計算の性質はサブクラスには知られません。オーバーライドするプロパティの名前と型の両方を常に指定して、オーバーライドが同じ名前と型を持つスーパークラスプロパティと一致することをコンパイラが確認できるようにする必要があります。

サブクラスプロパティのオーバーライドでゲッターとセッターの両方を提供することで、継承された読み取り専用プロパティを読み書き可能なプロパティとして提示できます。ただし、継承された読み書き可能なプロパティを読み取り専用プロパティとして提示することはできません。

注:プロパティオーバーライドの一部としてセッターを提供する場合、そのオーバーライドのためにゲッターも提供する必要があります。オーバーライドされたゲッター内で継承されたプロパティの値を変更したくない場合は、オーバーライドされたゲッターから super.someProperty を返すことで継承された値を単に通過させることができます。ここで、someProperty はオーバーライドするプロパティの名前です。

次の例は、Vehicle のサブクラスである新しいクラス Car を定義します。Car クラスは、デフォルトの整数値が1の新しいストアドプロパティ gear を導入します。Car クラスは、現在のギアを含むカスタム説明を提供するために、Vehicle から継承する description プロパティもオーバーライドします:

swift
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 クラス内で定義されたカスタム説明を返すことがわかります:

swift
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 クラスは、現在の速度に基づいて適切なギアを自動的に選択する自動変速機を持つ車を表します:

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

AutomaticCar インスタンスの currentSpeed プロパティを設定するたびに、プロパティの didSet オブザーバーがインスタンスの gear プロパティを新しい速度に適したギアに設定します。具体的には、プロパティオブザーバーは新しい currentSpeed 値を10で割り、最も近い整数に切り捨て、1を加えたギアを選択します。速度が35.0の場合、ギアは4になります:

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

オーバーライドの防止

メソッド、プロパティ、またはサブスクリプトを final としてマークすることで、オーバーライドを防ぐことができます。これを行うには、メソッド、プロパティ、またはサブスクリプトの導入キーワード(final varfinal funcfinal class func、および final subscript など)の前に final 修飾子を記述します。

サブクラスで final メソッド、プロパティ、またはサブスクリプトをオーバーライドしようとすると、コンパイル時エラーとして報告されます。拡張機能にクラスに追加するメソッド、プロパティ、またはサブスクリプトも、拡張機能の定義内で final としてマークできます。詳細については、拡張機能を参照してください。

クラス全体を final としてマークするには、クラス定義の class キーワードの前に final 修飾子を記述します(final class)。final クラスをサブクラス化しようとすると、コンパイル時エラーとして報告されます。