Warum bindet Java Variablen zur Kompilierzeit?

Lesezeit: 4 Minuten

Betrachten Sie den folgenden Beispielcode

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();
    }
}

es gibt die folgende Ausgabe

base
base
base
derived

Methodenaufrufe werden zur Laufzeit aufgelöst und die richtige überschriebene Methode wird wie erwartet aufgerufen.
Der Variablenzugriff wird stattdessen zur Kompilierzeit aufgelöst, wie ich später erfuhr. Ich hatte eine Ausgabe als erwartet

base
derived
base
derived

weil in der abgeleiteten Klasse die Neudefinition von var beschattet die in der Basisklasse.
Warum erfolgt die Bindung von Variablen zur Kompilierzeit und nicht zur Laufzeit? Ist das nur aus Performance-Gründen?

  • Eine Randnotiz für eine ähnliche Sprache: In C# haben Sie in allen Fällen eine Bindung zur Kompilierzeit, es sei denn, Sie verwenden dies ausdrücklich virtual und override. Und Sie können sie nicht für Variablen verwenden.

    – edc65

    6. September 2015 um 19:48 Uhr


  • Siehe auch: stackoverflow.com/questions/13748124/…

    – Benutzer11153

    7. September 2015 um 6:46 Uhr

Benutzer-Avatar
MA

Der Grund wird in der Java Language Specification in einem Beispiel erläutert Abschnitt 15.11nachfolgend zitiert:

Die letzte Zeile zeigt, dass das Feld, auf das zugegriffen wird, tatsächlich nicht von der Laufzeitklasse des referenzierten Objekts abhängt; selbst wenn s enthält eine Referenz auf ein Objekt der Klasse Tder Ausdruck s.x bezieht sich auf x Bereich der Klasse Sweil der Typ des Ausdrucks s ist S. Objekte der Klasse T enthalten zwei Felder mit dem Namen xeine für die Klasse T und einer für seine Oberklasse S.

Dieses Fehlen einer dynamischen Suche nach Feldzugriffen ermöglicht es, Programme effizient mit unkomplizierten Implementierungen auszuführen. Die Möglichkeit des späten Bindens und Überschreibens ist verfügbar, aber nur, wenn Instanzmethoden verwendet werden

Also ja, Leistung ist ein Grund. Die Spezifikation, wie der Feldzugriffsausdruck ausgewertet wird, ist wie folgt angegeben:

  • Wenn das Feld nicht ist static:

    • Wenn das Feld nicht leer ist finaldann ist das Ergebnis der Wert des benannten Mitgliedsfelds in type T gefunden in dem Objekt, auf das durch den Wert von verwiesen wird Primär.

wo Primär in Ihrem Fall bezieht sich die Variable derived was vom Typ ist MyClass.

Ein weiterer Grund, wie @Clashsoft vorgeschlagen hat, ist, dass Felder in Unterklassen nicht überschrieben werden, sie sind es versteckt. Daher ist es sinnvoll, den Zugriff auf die Felder basierend auf dem deklarierten Typ oder mithilfe einer Umwandlung zuzulassen. Dies gilt auch für statische Methoden. Aus diesem Grund wird das Feld basierend auf dem deklarierten Typ bestimmt. Im Gegensatz zum Überschreiben durch Instanzmethoden, bei denen es vom tatsächlichen Typ abhängt. Das obige JLS-Zitat erwähnt diesen Grund tatsächlich implizit:

Die Möglichkeit des späten Bindens und Überschreibens ist verfügbar, aber nur, wenn Instanzmethoden verwendet werden.

Benutzer-Avatar
Clashsoft

Während Sie in Bezug auf die Leistung Recht haben könnten, gibt es einen weiteren Grund, warum Felder nicht dynamisch verteilt werden: Sie könnten nicht auf die zugreifen MyClass.var Feld überhaupt, wenn Sie eine hatten MyDerivedClass Beispiel.

Im Allgemeinen kenne ich keine statisch typisierte Sprache, die tatsächlich eine dynamische Variablenauflösung hat. Aber wenn Sie es wirklich brauchen, können Sie Getter oder Accessor-Methoden erstellen (was in den meisten Fällen getan werden sollte, um es zu vermeiden public Felder sowieso):

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;
    }
}

  • Viel besser als meine Antwort gewesen wäre: Weil die Sprache so konzipiert wurde.

    –Andreas

    6. September 2015 um 11:22 Uhr

  • “Sie könnten überhaupt nicht auf das Feld MyClass.var zugreifen, wenn Sie eine MyDerivedClass-Instanz hätten.” Dasselbe gilt für Methoden, also ist dies überhaupt kein Grund.

    – Jens Schauder

    6. September 2015 um 11:50 Uhr

  • @JensSchauder stimmt, aber für Methoden wissen Sie immer, dass Sie die andere Methode überschreiben, und das ist das bekannteste Hauptmerkmal von OO.

    – Clashsoft

    6. September 2015 um 15:11 Uhr


  • Wenn ich Ihre Definition von “dynamischer Variablenauflösung” nicht falsch verstehe, fallen mir viele Sprachen ein, die dies haben – dynamische Sprachen wie Python oder JavaScript lösen Variablen normalerweise zur Laufzeit auf, und sogar Scala (eine statische Sprache) sucht Variablen polymorph.

    – James_pic

    7. September 2015 um 9:36 Uhr

  • Ich habe bearbeitet, um nur statisch typisierte Sprachen einzuschließen. Scala hängt von der JVM ab und “betrügt” diese, indem es eine Zugriffsmethode für Felder generiert. Ich weiß nicht, ob dieses Überschreibungsverhalten von ihren Compiler-Entwicklern beabsichtigt ist.

    – Clashsoft

    9. September 2015 um 15:16 Uhr

Benutzer-Avatar
alainlompo

Das polymorphe Verhalten der Java-Sprache arbeitet mit Methoden und nicht mit Member-Variablen: Sie haben die Sprache entworfen, um Member-Variablen zur Kompilierzeit zu binden.

In Java ist dies beabsichtigt. Denn das Einrichten von Feldern, die dynamisch aufgelöst werden sollen, würde die Dinge etwas langsamer machen. Und in Wirklichkeit gibt es keinen Grund dafür. Da kannst du deine Felder in jeder Klasse machen Privatgelände und greife mit darauf zu Methoden welche sind dynamisch aufgelöst.

So werden Felder besser aufgelöst Kompilierzeit stattdessen 🙂

1116830cookie-checkWarum bindet Java Variablen zur Kompilierzeit?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy