Javaにおける動的バインディングのメカニズムは、コンパイル時ではなく実行時にどのメソッドが呼び出されるかを決定します。静的バインディングとの主な違いは、プライベート、スタティック、ファイナルメソッドなどが静的にバインドされ、実行時にオーバーライドされることがない(バインディングがコンパイル時に行われる)のに対し、動的にはオーバーライドされた通常のインスタンスメソッドがバインドされることです。これにより多態性が可能になります。
例:
class Animal { void makeSound() { System.out.println("Some sound"); } } class Dog extends Animal { void makeSound() { System.out.println("Bark"); } } public class Test { public static void main(String[] args) { Animal a = new Dog(); a.makeSound(); // 出力: Bark (動的バインディング) } }
ここでは、makeSound()メソッドが動的にバインドされます - JVMは実行時にどのバージョンを呼び出すかを決定します。
質問: "インターフェースまたは抽象クラスの型の変数を宣言し、子クラスのインスタンスを代入した場合、オーバーライドされたメソッドにアクセスするときにどのメソッドが呼び出されますか?コンパイラはこれをどのように決定しますか?"
よくある誤解: 多くの人はコンパイル時に参照の型でメソッドが決まると考えていますが、実際には実行時にJVMが行います。
正しい答え: JVMはメソッドを呼び出すために実際のオブジェクトの型を使用します(動的バインディング)が、参照の型ではありません。
Shape s = new Circle(); s.draw(); // Circleからのdraw()が呼び出され、Shapeからは呼び出されません
物語
大手銀行のプロジェクトで、ある開発者がtoString()とequals()メソッドをオーバーライドし、インターフェース経由で変数を宣言することでインターフェースのメソッドが呼び出されると信じていました。その結果、オブジェクトの比較が正しく行われず、参照が比較され、値が比較されることがなくなり、重複を探す際のクライアントの比較の論理に誤りをもたらしました。
物語
eコマースプロジェクトで、開発者は基本クラスProductを作成し、ElectronicProductを継承しました。Product[]配列には両方のタイプのオブジェクトが保存されていました。ElectronicProductでオーバーライドされたメソッドを通じて製品を表示すると、基本クラスの情報のみが表示されました。なぜなら静的メソッドが呼び出されたからです!この誤りは出版前に発見されました。
物語
交通のモデリングプロジェクトではFactoryパターンが使用されました。開発者はメソッドではなくフィールドを扱っていることに気づかず、派生クラスのフィールドにアクセスすると基本クラスの値が返されるのに対し、オーバーライドされたものではなく、フィールドは多態的ではなかったためです!システムはすべてのオブジェクトに"Transport"という型を表示し、ルートの計算を不正確に行いました。