Springを学ぶときに、このような疑問が生じることがあるかもしれません。私たちはここでインターフェースの抽象メソッドを呼び出しているだけなのに、なぜ実行時にそのインターフェースの実装クラスのメソッドが実行されるのでしょうか?
直接に答えると:ここではJavaの三大特徴の一つであるポリモーフィズムが使われています。
ポリモーフィズムはオブジェクト指向プログラミングの重要な特徴の一つです。ポリモーフィズムを通じて、親クラス(またはインターフェース)の参照変数は、任意の子クラス(または実装クラス)のインスタンスを指すことができます。 これにより、より汎用的で柔軟なコードを書くことができます。
Javaの三大特徴は、カプセル化、継承、そしてポリモーフィズムです。
理解しやすくするために、まず「ポリモーフィズム」の概念を振り返りましょう。
ポリモーフィズムの核心:コンパイル時には左側(親クラスの参照)を見て、実行時には右側(子クラスのオブジェクト)を見ます。
コンパイル時:「左側を見ます」
コンパイル時に、コンパイラはコードの文法と型が正しいかどうかをチェックします。変数を宣言するとき、コンパイラはその型を記憶します。ポリモーフィズムの場合、この型は親クラスの型です。 例えば:
Parent obj = new Child();
この行のコードでは、obj の宣言型は Parent です。コンパイラはこの型に基づいて、obj があるメソッドを呼び出すことができるか、あるいは特定の属性にアクセスできるかをチェックします。つまり、コンパイラは Parent クラスに宣言されているメソッドや属性のみをチェックします。
例えば、Parent クラスに show() メソッドがあり、Child クラスがこのメソッドをオーバーライドしている場合:
class Parent {
void show() {
System.out.println("Parent's show()");
}
}
class Child extends Parent {
@Override
void show() {
System.out.println("Child's show()");
}
}
次のコードをコンパイル時に書くと:
Parent obj = new Child();
obj.show();
コンパイラは obj の宣言型 Parent をチェックし、Parent クラス に show() メソッドが確かに存在することを確認します。したがって、このコードは合法であり、コンパイルが通ります。
しかし、Child クラスにのみ存在し、Parent クラスには存在しないメソッドを呼び出そうとすると、例えば:
Parent obj = new Child();
obj.childOnlyMethod(); // このメソッドが Child クラスにのみ宣言されていると仮定
コンパイラはエラーを報告します。なぜなら、childOnlyMethod() はParent クラスに存在しないからです:
error: cannot find symbol
実行時:「右側を見ます」
実行時には、Java仮想マシン(JVM)がコードを実行します。このとき、obj は実際には Child オブジェクトを参照しています。 したがって、obj.show() を呼び出すと、JVM は obj の実際の型(Child)に基づいて Child クラスでオーバーライドされた show() メソッドを呼び出します。これにより、Parent クラスの show() メソッドではなく、Child クラスの show() メソッドが実行されます。
例のまとめ
class Parent {
void show() {
System.out.println("Parent's show()");
}
}
class Child extends Parent {
@Override
void show() {
System.out.println("Child's show()");
}
}
public class Test {
public static void main(String[] args) {
Parent obj = new Child(); // 宣言型は Parent、実際の型は Child
obj.show(); // 出力: Child's show()
}
}
この例では:
1.コンパイル時:
- コンパイラは obj の宣言型が Parent であることをチェックします
- Parent クラスに show() メソッドがあることを確認し、コンパイルが通ります
2.実行時:
- JVM は obj が実際には Child オブジェクトを参照していることを知っています
- obj.show() を呼び出すとき、実際に実行されるのは Child クラスでオーバーライドされた show() メソッドです。したがって、Child's show() が出力されます
これがポリモーフィズムの核心です:コンパイル時は左側(親クラスの参照)を見て、実行時は右側(子クラスのオブジェクト)を見ます。
こちらをご覧いただければ、最初に提起した質問を完全に理解いただけると思います。最後に、Springにおいてポリモーフィズム(多態性)がどのように機能するかを明確にしましょう。
長い記事に最後までお付き合いいただきありがとうございました。ご質問や異論があれば、コメントをお願いします。