10

これは純粋に理論的な質問です。

3 つの単純なクラスが与えられた場合:

class Base {
}

class Sub extends Base {
}    

class SubSub extends Sub {
}

そして、これらのクラスを操作するための関数:

public static void doSomething(Base b) {
  System.out.println("BASE CALLED");
}
public static void doSomething(Sub b) {
  System.out.println("SUB CALLED");
}

次のコードのようです:

SubSub ss = new SubSub();
doSomething(ss);

SubSub はこれらの両方にキャストできるため、正当に BASE CALLED または SUB CALLED のいずれかを出力する可能性があります。実際、関数の Sub バージョンを削除すると、BASE CALLED が出力されます。実際に起こることは、「SUB CALLED」が出力されることです。これは、Base バージョンが最初に呼び出されたため、どの関数が呼び出されるかは、関数が定義されている順序に依存しないことを意味しているようです。

Java は、関数のさまざまなバージョンをすべて調べて、継承スタックのトラバーサルが最も少ないバージョンを選択するだけですか? これは標準化されていますか?ドキュメントに記載されていますか?

4

4 に答える 4

9

正式な仕様は、Java 言語仕様 (JLS) のパート 15.12.2.5 にあります。ジェネリックのおかげでこれはかなり複雑になっているので、 JLS の初版の同じセクションを見たいと思うかもしれません。

基本的に、コンパイラは、メソッドが呼び出されるオブジェクトを含むすべてのパラメーターが最も具体的なメソッドのバージョンを見つけようとすることを示しています。そのようなメソッドが存在しない場合 (たとえば、method(Base, Sub)andmethod(Sub, Base)はあるが、ないためmethod(Sub, Sub))、コンパイルは失敗します。

メソッドの実際の選択は、パラメーターではなく、インスタンス メソッドのターゲット オブジェクトの動的な型に依存することに注意してください。あなたの例は、インスタンスレベルでも同じように機能します。

の型をキャストまたは再宣言することで、コンパイラに手を貸すことができるはずですss。変数の宣言された型が署名と正確に一致する場合、コンパイラーと保守プログラマーにとってもすべてが明らかです。宣言された型が一致する限り、より具体的な型を割り当てても問題ありません。

于 2008-12-22T05:14:35.987 に答える
2

私の知る限り、Java と C++ はコンパイル時にこの決定を行います (動的にディスパッチできない静的関数であるため)。静的型が SubSub で、SubSub を受け取るオーバーロードがある場合、これが呼び出されます。両方の基準にあると確信しています。

Sub または SubSub が含まれている場合でも、Base への参照またはポインターがある場合は、Base を使用するバージョンと一致します。これは、コンパイル時にそれがコンパイラーが持つ唯一の保証であるためです。

于 2008-12-22T04:31:08.040 に答える
0

静的メソッドをオーバーロードすると、メソッドを呼び出しているクラスですぐに定義されたメソッドが呼び出されます。ただし、呼び出し元のクラスでメソッドが定義されていない場合は、直接の親クラスから継承されたメソッドが呼び出されます。

あなたの場合、2 つのオーバーロードされたメソッドがあり、どちらも SubSub をパラメーターとして受け入れることができます。コンパイラは最も具体的な一致をチェックし、それを探します。ただし、最も具体的な一致は、通常、型階層の最下位です。

編集済み

矛盾するステートメントを削除しました。同じ型階層レベルにあるクラス内の 2 つのメソッドを、コンパイラが選択できるようにあいまいな状態にすることはできません。このあいまいさは、多重継承の場合にのみ可能です。

于 2008-12-22T04:51:38.140 に答える