【Swift】クラスの継承、メソッドのオーバーライドの方法。(Swift 2.1、XCode 7.2)
クラスの継承とは
他のオブジェクト指向言語と同様に、Swiftにもクラスの継承という機能がある。
クラスの継承とは、あるクラスのプロパティ、メソッドを引き継ぎながら新しいクラスを作成することをいう。
例えば、ハンバーガーショップの社員を表すクラスに接客クラスとコッククラスというのがあるとする。接客クラスとコッククラスが持っているプロパティで「社員番号」、「名前」など両方のクラスに共通してあるものがあれば、「注文を受ける」という接客クラスならではのメソッドや、「ハンバーガーを作る」というコッククラスならではのメソッドがある。
そこで、両方のクラスに共通するプロパティやメソッドを集めたクラス(スーパークラス)を作り、スーパークラスの機能を引き継ぐかたちで接客クラス、コッククラスを作ることをクラスの継承という。スーパークラスを継承したクラスのことをサブクラスといい、サブクラスはスーパークラスの全ての機能を利用できる。
もし、同じプロパティやメソッドがすべてのクラスに記述されていると、コードの修正が必要になった場合にすべてのクラスのコードを修正するはめになり、多大の作業量とバグを混入させる危険がある。
このようなときにクラスの継承を使えば、コード量を減らし、修正箇所を局所化できるためメンテナンスがしやすくなる。
サブクラスを作る
サブクラスは以下のように定義する。1つのサブクラスにつき、スーパークラスは1個しか定義できない。
0 1 2 3 4 |
class クラス名:スーパークラス名 { プロパティ メソッド } |
以下のコードは社員クラスの定義したあと、社員クラスを継承して接客クラスを作った例である。社員クラスで定義したプロパティやメソッドに接客クラスのインスタンスからアクセスできている。
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 32 33 34 35 |
/* ** クラスの継承 */ //社員クラス class Employee { let id:Int //社員番号 let name:String //名前 //イニシャライザ init(id:Int, name:String, tantou:String = "共通"){ //引数の値をインスタンスの変数に代入する self.id = id self.name = name } } //接客クラス class Sekkyaku:Employee { //注文を受ける func reciveOrder() -> Bool{ //とりあえず処理は省略 return true } } //接客クラスのインスタンスを生成して変数に格納する。 var employee1 = Sekkyaku(id:77, name:"佐藤", tantou:"ホールスタッフ") print(employee1.id) //実行結果 //77 |
先ほど、1クラスに1個しかスーパークラスを定義できないと説明したが、場合によっては、2つのクラスのプロパティとメソッドを継承したいクラスを作りたいと思うときがある。
そんなときは以下のコードのようにクラスを継承したクラスを作り、さらに、そのクラスを継承するクラスを作ることで2つのクラスを継承したクラスにできる。
以下の例ではクラス2はクラス1を継承し、クラス3はクラス2を継承させた。その結果、クラス3はクラス1、クラス2のどちらで定義されたメソッドも使うことができる。
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 32 33 34 35 36 37 38 39 40 41 42 43 44 |
/* ** クラスの継承 */ //クラス1 class TestClass1 { let test1:Int = 1 func testOutput1() { print("テストメッセージ\(test1)") } } //クラス2(クラス1を継承) class TestClass2 : TestClass1 { let test2:Int = 2 func testOutput2() { print("テストメッセージ\(test2)") } } //クラス3(クラス2を継承) class TestClass3 : TestClass2 { let test3:Int = 3 func testOutput3() { print("テストメッセージ\(test3)") } } //TestClass3インスタンスのメソッドを実行する。 var test = TestClass3() test.testOutput1() test.testOutput2() test.testOutput3() //実行結果 //テストメッセージ1 //テストメッセージ2 //テストメッセージ3 |
オーバーライド
クラスを継承したものの、あるメソッドは、スーパークラスのではなく自分で書いたメソッドを使いたい場合がある。
そのようなときは、以下のコードのようにoverrideを先頭に書いてメソッドを定義することで、スーパークラスの関数を上書きできる。これをオーバーライドという。
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 32 33 34 35 36 |
/* ** クラスメソッドのオーバーライド */ //クラス1 class TestClass1 { func sayGoodmorning() { print("今日はいい天気ですね。") } func sayGoodNight() { print("おやすみなさい。") } } //クラス2(クラス1を継承) class TestClass2 : TestClass1{ //メソッドをオーバーライド override func sayGoodmorning() { print("おはようございます。") } } //クラス2インスタンスのメソッドを実行する var test = TestClass2() test.sayGoodmorning() test.sayGoodNight() //実行結果 //おはようございます。 //おやすみなさい。 |
なお、スーパークラスと同じメソッド名のものをoverrideをつけずにサブクラスで定義するとコンパイルエラーになる。
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 |
/* ** オーバーライドの失敗例 */ //クラス1 class TestClass1 { func sayGoodmorning() { print("今日はいい天気ですね。") } } //クラス2(クラス1を継承) class TestClass2 : TestClass1{ func sayGoodmorning() { print("おはようございます。") } } //コンパイルエラー //error: overriding declaration requires an 'override' keyword |
逆に、スーパークラスに無いメソッド名をサブクラスでoverrideをつけて定義してもコンパイルエラーにしてくれる。親切だ。
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 |
/* ** オーバーライドの失敗例 */ //クラス1 class TestClass1 { func sayGoodNight() { print("おやすみなさい。") } } //クラス2(クラス1を継承) class TestClass2 : TestClass1{ override func sayGoodmorning() { print("おはようございます。") } } //コンパイルエラー //error: method does not override any method from its superclass |
スーパークラスのメソッドを呼び出す
スーパークラスのメソッドをオーバーライド(上書き)したけど、上書き前のメソッドを呼び出したいときがある。
そんなときは、以下のコードのようにsuperに対してオーバーライドしたメソッドを呼び出す。superとはスーパークラスのことを意味している。メソッドをオーバーライドしてもスーパークラスのメソッドが完全に消えてしまうわけでは無いのだ。
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 32 33 34 35 36 37 38 39 40 41 |
/* ** スーパークラスのメソッドを呼び出す。 */ //クラス1 class TestClass1 { func sayGoodmorning() { print("今日はいい天気ですね。") } } //クラス2(クラス1を継承) class TestClass2 : TestClass1{ //メソッドのオーバーライド override func sayGoodmorning() { print("おはようございます。") } func sayAll() { //自インスタンスのメソッドを呼び出す。 sayGoodmorning() //スーパークラスのメソッドを呼び出す。 super.sayGoodmorning() } } //クラス2インスタンスのメソッドを実行する var test = TestClass2() test.sayAll() //実行結果 //おはようございます。 //今日はいい天気ですね。 |
継承を禁止する
今までの話を聞くと、プログラムを作る人は必要なクラスがあればどんなクラスを好きなだけ継承できるように感じるが、クラスによっては、安全な作りにするために、継承させたくないクラスが出てくることがある。
そのようなときは、finalをつけてクラス宣言すると他のクラスが継承することができないクラスを記述することができる。
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 |
/* ** クラスの継承を禁止する。 */ //クラス1(final宣言) final class TestClass1 { func sayGoodmorning() { print("今日はいい天気ですね。") } } //クラス2(クラス1を継承しようとするが。。) class TestClass2 : TestClass1{ func sayGoodNight() { print("おやすみなさい。") } } //実行結果 //error: inheritance from a final class 'TestClass1' |