web-dev-qa-db-ja.com

Javaコンパイル時に変数をバインドするのはなぜですか?

次のサンプルコードを考えてみましょう

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の再定義により、基本クラスの再定義が隠されます。
なぜ変数のバインディングが実行時ではなくコンパイル時に行われるのですか?これはパフォーマンス上の理由のみですか?

63

その理由は、Java言語仕様の例 セクション15.11 で説明されており、以下に引用されています):

...

最後の行は、実際に、アクセスされるフィールドが参照されるオブジェクトのランタイムクラスに依存しないことを示しています。 sがクラスTのオブジェクトへの参照を保持している場合でも、式s.xは、クラスxSフィールドを参照します。これは、式sのタイプがSであるためです。クラスTのオブジェクトには、xという名前の2つのフィールドが含まれています。1つはクラスT用で、もう1つはそのスーパークラスSです。

フィールドアクセスの動的ルックアップがないため、簡単な実装でプログラムを効率的に実行できます。レイトバインディングとオーバーライドの機能は利用できますが、インスタンスメソッドが使用される場合のみ...

はいパフォーマンスは理由です。フィールドアクセス式の評価方法の仕様は次のとおりです。

  • フィールドがstaticではない場合:

    ...

    • フィールドが空白でないfinalの場合、結果はの値によって参照されるオブジェクトで見つかった、タイプTの名前付きメンバーフィールドの値です。プライマリ

ここで、 Primary は、derived型の変数MyClassを参照します。

@Clashsoftが示唆するもう1つの理由は、サブクラスではフィールドがオーバーライドされず、 hidden になるためです。そのため、宣言された型に基づいて、またはキャストを使用して、どのフィールドにアクセスできるようにするのが理にかなっています。これは静的メソッドにも当てはまります。これが、宣言された型に基づいてフィールドが決定される理由です。実際の型に依存するインスタンスメソッドによるオーバーライドとは異なります。上記のJLSの引用は、この理由を暗黙的に述べています。

遅延バインディングとオーバーライドの機能は利用できますが、インスタンスメソッドが使用される場合のみです。

45
manouti

パフォーマンスは正しいかもしれませんが、フィールドが動的にディスパッチされないもう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;
    }
}
25
Clashsoft

Java言語の多態的な動作は、メンバー変数ではなくメソッドで動作します。それらは、コンパイル時にメンバー変数をバインドするように言語を設計しました。

5
alainlompo

Javaでは、これは仕様によるものです。動的に解決されるフィールドの設定により、実行が少し遅くなるからです。そして実際には、そうする理由は何もありません。以来、任意のクラスprivateでフィールドを作成し、動的に解決されるであるmethodsを使用してそれらにアクセスできます。

したがって、フィールドはコンパイル時で解決されるようになり、代わりに:)

2