クラスの継承で特筆すべきなのは、単純な機能の継承ではありません。 クラスの継承は抽象的な機能を具体的な処理に移行する、機能の上書きにあると考えられます。 この、機能の上書きとは、スーパークラスのメソッドを隠蔽し、サブクラスのメソッドを優先低に呼び出させる、メソッドの上書きによって実現します。 これを、一般にはオーバーライドと呼びます。
メソッドのオーバーライドは、スーパークラスのメソッドと同じ名前、同じ戻り値型、同じ引数のメソッドをサブクラスで定義することで実現します。 一般には、このようなメソッド名とメソッドの戻り値や引数型全体をシグネチャと呼びます。 メソッドのオーバーライドは、シグネチャが同じメソッドをサブクラスで定義することで実現するのです。
#import <stdio.h> #import <objc/Object.h> @interface SuperClass : Object - (void)method; @end @interface SubClass : SuperClass -(void)method; @end @implementation SuperClass - (void)method { printf("SuperClass.method\n"); } @end @implementation SubClass - (void)method { printf("SubClass.method\n"); } @end int main() { [[SuperClass alloc] method]; [[SubClass alloc] method]; return 0; }
このプログラムでは、スーパークラスである SuperClass クラスとサブクラスの SubClass クラスでシグネチャが同一の method メソッドを宣言、及び定義しています。 Objective-C では、このようなメソッドの衝突はコンパイルエラーではなく、メソッドの上書き、すなわちオーバーライドすることで解決します。
オーバーライドされたメソッドの効果は、main() メソッドの実行結果を見ると確認することができます。 インスタンスが SuperClass の場合は SuperClass クラスの method メソッドが呼び出され、インスタンスが SubClass クラスのものであれば SubClass の method メソッドが呼び出されます。 これは、SubClass が SuperClass の method メソッドを隠し、新しく定義した自らの method メソッドでオーバーライドしたことを表します。
オーバーライドは、結果としてプログラムの多様性を実現します。 メソッドを呼び出すコードは、コードを変更しなくても、インスタンスによって呼び出すべき適切なメソッドを選択することができるようになるのです。 次のようなコードを想定してください。
void CallMethod(id obj) {
[obj method];
}
int main() {
CallMethod([SuperClass alloc]);
CallMethod([SubClass alloc]);
return 0;
}
このコードでは、CallMethod() 関数で id 型のオブジェクトを引数から受け取っています。 この関数では、受け取ったオブジェクトの method メソッドを呼び出しています。
しかし、この関数が結果的にどのメソッドを呼び出すかは、渡されるオブジェクトのインスタンスによって異なります。 関数が呼び出すメソッドは、実行時にならなければ最終的な判定はできません。 C 言語では、switch 文などで分岐させて呼び出すべき関数を選別したり、実行時に動的に関数を呼び出す仕組みを実現するために、関数へのポインタなどを利用しなければなりませんでした。 Objective-C では、そのような独自の設計は必要なく、動的な呼び出しによるコードの多様性を言語レベルでサポートしているのです。
ところが、スーパークラスのメソッドが完全に隠蔽されても困ることがあります。 特に、メソッドのオーバーライドの目的が機能の拡張である場合、拡張する部分だけをサブクラスで記述し、残りの基本的な処理部分はスーパークラスのオーバーライドされたメソッドに委ねるべきです。 これを実現するには、サブクラスのオーバーライドしたメソッドから、スーパークラスのオーバーライドされたメソッドを呼び出す必要があります。
スーパークラスのメソッドをサブクラスから明示的に呼び出すには super を利用します。 super は、スーパークラスを保有するクラスのインスタンスメソッド内のメッセージ式でのみ利用できる特別な名前で、スーパークラスのメソッドを呼び出すことを明示します。 self はインスタンスメソッド内に存在する暗黙的な id 型の変数でしたが、super は変数ではないことに注意してください。
#import <stdio.h> #import <objc/Object.h> @interface SuperClass : Object { int x; } - (void)method; @end @interface SubClass : SuperClass -(void)method; @end @implementation SuperClass - (void)method { printf("SuperClass.method\n"); } @end @implementation SubClass - (void)method { printf("SubClass.method %d\n" , x); [super method]; } @end int main() { [[SubClass alloc] method]; return 0; }
このプログラムでは、SuperClass のメソッドをオーバーライドしている SubClass の method メソッドから、スーパークラスのオーバーライドされた method メソッドを呼び出すために super を利用しています。 実行結果から、SubClass の method から SuperClass の method を呼び出していることが確認できるでしょう。