Programmingバックエンド開発者

Javaにおける継承時のメソッド解決順序(MRO)がどのように機能するか、またどのような落とし穴があるかを説明してください。

Hintsage AIアシスタントで面接を突破

回答。

Javaでは、メソッドのオーバーロード(overloading)とオーバーライド(overriding)は特定のルールに従って解決されます。オーバーロードの際、コンパイラは呼び出し元の引数の型に基づいて、コンパイル時に最も適切なメソッドを選択します。

重要なポイント:

  • すべてのクラスおよびスーパークラスにオーバーロードメソッドが存在する場合、まず現在のクラスでの最適な一致が検索され、次にスーパークラスで検索されます。
  • メソッドの選択は実行時ではなくコンパイル時に行われます。
  • 型の変換(widening、オートボクシング、varargs)は必要に応じて適用されます。

例:

class Super { void print(Number n) { System.out.println("Super Number"); } } class Sub extends Super { void print(Integer i) { System.out.println("Sub Integer"); } } ... Sub s = new Sub(); s.print(5); // Sub Integer s.print(5.0); // Super Number

ここでは、print(Integer)がInteger型の引数に対して呼び出され、Double型に対しては親メソッドが呼び出されます。

トリックな質問。

サブクラスでメソッドをオーバーライド(override)し、オーバーロード(overload)した場合、ポリモーフィズムの際にどちらが呼び出されますか?

回答: ポリモーフィズムでは、オーバーライドされたメソッドの場合、常にオブジェクトの実際の型に基づいてメソッドのバージョンが選ばれます。オーバーロードされたメソッドの場合、大文字の型参照に基づいてコンパイル時に選択が行われます。

class A { void test(Number n) { System.out.println("A:Number"); } } class B extends A { void test(Integer n) { System.out.println("B:Integer"); } @Override void test(Number n) { System.out.println("B:Number"); } } ... A obj = new B(); obj.test(1); // B:Numberが呼び出される、test(Integer)が存在しても

このテーマの細部を知らないことによる実際のエラーの例。


物語

開発者はサブクラスにオーバーロードされたバージョンのメソッドを追加し、すべてのサブクラスに対して呼び出されることを期待していました。しかし、親クラスの参照を通じて呼び出すと、オブジェクトの型ではなく参照の型に基づいてメソッドが選択され、必要なロジックが実行されませんでした。


物語

大規模なプロジェクトでequals(Object)メソッドを誤ってequals(MyClass obj)というシグネチャでオーバーロードし、標準のequalsを置き換えると思っていました。HashSetのコレクションで比較すると、デフォルトのequals(Object)が動作し、ロジックの不一致とデータの損失を引き起こしました。


物語

varargsを持つメソッドの新しいバージョンをクラス階層に追加すると、配列に対して必要なバージョンが呼び出されると思われました。しかし、配列を明示的に渡すと、コンパイラが間違ったオーバーロードを選択し、結果として処理が不正確になり、バグが長い間見逃されていました。