見出し画像

今でしょ!Swift - 継承

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

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

継承ができるのはクラスのみです。

クラスは、スーパークラスに属するメソッド、プロパティ、およびサブスクリプトを呼び出してアクセスできます。クラスのメソッド、プロパティ、およびサブスクリプトの独自のオーバーライドを変更できたりします。オーバーライドしている定義がスーパークラスの定義に一致をチェックする仕組みがあります。

クラスは、継承したプロパティにプロパティオブザーバを追加することで、プロパティの値が変更されたときに通知を受けることができます。

プロパティオブザーバは、元々格納プロパティとして定義されているか計算プロパティとして定義されているかに関係なく、任意のプロパティに追加できます。

基本クラスの定義

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

NOTE
Swift は、全てのクラスで共通する基本クラスを継承しません。スーパークラスを指定せずに定義したクラスは、自動的に基本クラスになります。

以下の例では、Vehicle という基本クラスを定義しています。この基本クラスは、currentSpeed と呼ばれる格納プロパティを定義します。デフォルト値は 0.0 です(プロパティの型は Double と推論されます)。

currentSpeed プロパティの値は、description と呼ばれる読み取り専用の String 型の計算プロパティに使用され、乗り物の説明をします。

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

class Vehicle {
    var currentSpeed = 0.0
    var description: String {
        return "走行時速 \(currentSpeed) マイル"
    }
    func makeNoise() {
        // 何もしない - 乗り物は必ずしも騒音を出しません
    }
}

使う時は、イニシャライザ構文を使用して Vehicle の新しいインスタンスを作成します。これは、型名とそれに続く空の括弧(())として記述されます。

let someVehicle = Vehicle()

新しい Vehicle インスタンスを作成したら、その description プロパティにアクセスして、乗り物の現在の速度の人間が読める形式の説明を出力します。

print("乗り物: \(someVehicle.description)")
// 乗り物: 走行時速 0.0 マイル

Vehicle クラスは、任意の乗り物に共通の特性を定義しますが、そのまま使用せずに具体的な種類の乗り物を記述して使います。

サブクラス化

サブクラス化は、既存のクラスに基づいて新しいクラスを作成することを指します。

サブクラスは既存のクラスの特性を継承し、それを変更すること、新しい特性を追加することもできます。

サブクラスにスーパークラスがあることを示すには、サブクラス名の後にコロン(:)で区切ってスーパークラス名を記述します

class SomeSubclass: SomeSuperclass {
    // サブクラスの定義をここに
}

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

class Bicycle: Vehicle {
    var hasBasket = false
}

新しい Bicycle クラスは、currentSpeed プロパティと description プロパティ、makeNoise() メソッドなど、Vehicle の全ての特性を自動的に継承します。

加えて、Bicycle クラスは新しい格納プロパティ hasBasket を定義し、デフォルト値は false です(プロパティは Bool 型と推論されます)。

デフォルトでは、作成する新しい Bicycle インスタンスにはカゴがありません。特定の Bicycle インスタンスが作成された後、そのインスタンスに対して hasBasket プロパティを true に設定できます。

let bicycle = Bicycle()
bicycle.hasBasket = true

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

bicycle.currentSpeed = 15.0
print("自転車: \(bicycle.description)")
// 自転車: 走行時速 15.0 マイル

サブクラス自体をサブクラス化できます。次の例では、「タンデム」と呼ばれる 2 人乗り自転車のサブクラスを作成します。

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.description)")
// タンデム: 走行時速 22.0 マイル


オーバーライド

サブクラスはのインスタンスメソッド、型メソッド、インスタンスプロパティ、型プロパティ、またはスーパークラスから継承するサブスクリプトの独自のカスタム実装をオーバーライドと呼びます。

継承した特性を正確に継承するためにオーバーライドするには、オーバーライドする定義の前に override キーワードを付けます。override キーワードを使用しないオーバーライドは、コンパイル時にエラーになります。

親クラスのメソッド、プロパティ、サブスクリプトへのアクセス

サブクラスにメソッド、プロパティ、またはサブスクリプトのオーバーライドを指定する場合、オーバーライドの一部として既存のスーパークラスの実装ができます(既存の実装の挙動を変更、変更された値を既存の継承した変数に格納)。

正しく実装されている場合、super プレフィックスを使用して、メソッド、プロパティ、またはサブスクリプトのスーパークラスバージョンにアクセスできます

  • someMethod() という名前のオーバーライドされたメソッドは、オーバーライドしたメソッドの実装内で super.someMethod() を呼び出すことにより、someMethod() のスーパークラスのバージョンを呼び出すことができます

  • someProperty と呼ばれるオーバーライドされたプロパティは、オーバーライドした get または set 実装内で super.someProperty として someProperty のスーパークラスのバージョンにアクセスできます

  • オーバーライドされた someIndex に対するサブスクリプトは、オーバーライドしたサブスクリプト実装内から、super[someIndex] で同じ someIndex に対するサブスクリプトのスーパークラスのバージョンにアクセスできます

メソッドのオーバーライド

継承したインスタンスまたは型メソッドをオーバーライドして、サブクラス内のメソッドを特定の目的、または代わりとなるように実装することができます。

次の例では、Train と呼ばれる Vehicle の新しいサブクラスを定義します。これは、Train が Vehicle から継承した makeNoise() メソッドをオーバーライドしています。

class Train: Vehicle {
    override func makeNoise() {
        print("シュッシュ、ポッポ")
    }
}

Train の新しいインスタンスを作成し、その makeNoise() メソッドを呼び出すと、Train サブクラスのバージョンのメソッドが呼び出すことができます。

let train = Train()
train.makeNoise()
// シュッシュ、ポッポ

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

継承したインスタンスまたは型プロパティをオーバーライドして、そのプロパティに独自の get/set を提供したり、プロパティオブザーバを追加して、基になるプロパティ値が変更されたときにオーバーライドしたプロパティを通してその値を監視できます。

get と set のオーバーライド

継承したプロパティが格納プロパティか計算プロパティかに関係なく、継承したプロパティをオーバーライドする独自の get (および必要ならば set )として使えます。

継承したプロパティの格納または計算プロパティの実装は、サブクラスには認識されません。継承したプロパティは、特定の名前と型を持っていることだけを認識します。

オーバーライドしたプロパティの名前と型の両方を常に指定して、そのオーバーライドが同じ名前と型のスーパークラスのプロパティとが一致する必要があります。

サブクラスで get/set の両方をオーバーライドすることにより、継承した読み取り専用プロパティを読み取り/書き込みプロパティにすることができます。ただし、継承した読み取り/書き込みプロパティを読み取り専用プロパティにすることはできません。

NOTE

プロパティのオーバーライドの一部として set を提供する場合は、get も提供する必要があります。オーバーライドした get 内で継承した値を変更したくない場合は、get から super.someProperty を返すことで、継承した値を渡すことができます。someProperty は、オーバーライドするプロパティの名前です。

次の例では、Vehicle のサブクラスの Car という新しいクラスを定義します。Car クラスは、デフォルトの整数値 1 で、gear と呼ばれる新しい格納プロパティを導入します。Car クラスは、Vehicle から継承する description プロパティもオーバーライドして、現在のギアを含むカスタムの説明をしています

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

description プロパティのオーバーライドは、Vehicle クラスの description プロパティを返す super.description を呼び出すことから始まり、新しいCar クラスの description は、現在のギアに関する情報を提供するために、この説明の最後にいくつかのテキストを追加しています。

Car クラスのインスタンスを作成し、その gear プロパティと currentSpeed プロパティを設定すると、description プロパティが Car クラス内で定義されたカスタマイズされた説明を返します。

let car = Car()
car.currentSpeed = 25.0
car.gear = 3
print("自動車: \(car.description)")
// 自動車: 走行時速 25.0 マイルでギアは 3

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

プロパティのオーバーライドを使用して、継承したプロパティのプロパティオブザーバを追加できます。これにより、継承したプロパティの値が変更されたときに、そのプロパティが最初にどのように実装されたかに関係なく通知を受け取ることができます。

NOTE
継承した定数の格納プロパティまたは継承した読み取り専用計算プロパティにプロパティオブザーバを追加することはできません。これらのプロパティの値は変更できないため、オーバーライドの一部として willSet または didSet の実装を提供することは適切ではありません。 同じプロパティに対して、set のオーバーライドとプロパティオブザーバのオーバーライドの、両方を提供することはできないことにも注意してください。プロパティの値の変更を監視する必要があり、そのプロパティのカスタムの set を既に提供している場合は、カスタムの set 内から値の変更が簡単に監視できます。

次の例では、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("オートマ車: \(automatic.description)")
// オートマ車: 走行時速 35.0 マイルでギアは 4

オーバーライドを防ぐ

メソッド、プロパティ、またはサブスクリプトをオーバーライドされないようにするにはメソッド、プロパティ、またはサブスクリプトのキーワードの前に final 修飾子を記述します(final var、final func、final class func や final subscript など)。

サブクラスで final メソッド、プロパティ、またはサブスクリプトをオーバーライドしようとすると、コンパイルエラーになります。extension でクラスに追加するメソッド、プロパティ、またはサブスクリプトにも、final をマークすることができます。

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

いいなと思ったら応援しよう!