次のサンプルコードを考えてみましょう
class MyClass {
public String var = "base";
public void printVar() {
System.out.println(var);
}
}
class MyDerivedClass extends MyClass {
public String var = "derived";
public void printVar() {
System.out.println(var);
}
}
public class Binding {
public static void main(String[] args) {
MyClass base = new MyClass();
MyClass derived = new MyDerivedClass();
System.out.println(base.var);
System.out.println(derived.var);
base.printVar();
derived.printVar();
}
}
次の出力が得られます
base
base
base
derived
メソッド呼び出しは実行時に解決され、期待どおりに、オーバーライドされた正しいメソッドが呼び出されます。
変数のアクセスは、後で学習したように、コンパイル時に解決されます。私は出力を期待していた
base
derived
base
derived
派生クラスでは、var
の再定義により、基本クラスの再定義が隠されます。
なぜ変数のバインディングが実行時ではなくコンパイル時に行われるのですか?これはパフォーマンス上の理由のみですか?
その理由は、Java言語仕様の例 セクション15.11 で説明されており、以下に引用されています):
...
最後の行は、実際に、アクセスされるフィールドが参照されるオブジェクトのランタイムクラスに依存しないことを示しています。
s
がクラスT
のオブジェクトへの参照を保持している場合でも、式s.x
は、クラスx
のS
フィールドを参照します。これは、式s
のタイプがS
であるためです。クラスTのオブジェクトには、x
という名前の2つのフィールドが含まれています。1つはクラスT
用で、もう1つはそのスーパークラスS
です。フィールドアクセスの動的ルックアップがないため、簡単な実装でプログラムを効率的に実行できます。レイトバインディングとオーバーライドの機能は利用できますが、インスタンスメソッドが使用される場合のみ...
はいパフォーマンスは理由です。フィールドアクセス式の評価方法の仕様は次のとおりです。
フィールドが
static
ではない場合:...
- フィールドが空白でない
final
の場合、結果はの値によって参照されるオブジェクトで見つかった、タイプT
の名前付きメンバーフィールドの値です。プライマリ。
ここで、 Primary は、derived
型の変数MyClass
を参照します。
@Clashsoftが示唆するもう1つの理由は、サブクラスではフィールドがオーバーライドされず、 hidden になるためです。そのため、宣言された型に基づいて、またはキャストを使用して、どのフィールドにアクセスできるようにするのが理にかなっています。これは静的メソッドにも当てはまります。これが、宣言された型に基づいてフィールドが決定される理由です。実際の型に依存するインスタンスメソッドによるオーバーライドとは異なります。上記のJLSの引用は、この理由を暗黙的に述べています。
遅延バインディングとオーバーライドの機能は利用できますが、インスタンスメソッドが使用される場合のみです。
パフォーマンスは正しいかもしれませんが、フィールドが動的にディスパッチされないもう1つの理由があります。MyDerivedClass
インスタンスがあると、MyClass.var
フィールドにまったくアクセスできなくなります。
一般に、実際に動的変数解決を行う静的型付け言語については知りません。しかし、本当に必要な場合は、ゲッターまたはアクセサメソッドを作成できます(とにかく、これは、ほとんどの場合public
フィールドを回避するために行う必要があります)。
class MyClass
{
private String var = "base";
public String getVar() // or simply 'var()'
{
return this.var;
}
}
class MyDerivedClass extends MyClass {
private String var = "derived";
@Override
public String getVar() {
return this.var;
}
}
Java言語の多態的な動作は、メンバー変数ではなくメソッドで動作します。それらは、コンパイル時にメンバー変数をバインドするように言語を設計しました。
Javaでは、これは仕様によるものです。動的に解決されるフィールドの設定により、実行が少し遅くなるからです。そして実際には、そうする理由は何もありません。以来、任意のクラスprivateでフィールドを作成し、動的に解決されるであるmethodsを使用してそれらにアクセスできます。
したがって、フィールドはコンパイル時で解決されるようになり、代わりに:)