Warum können wir nicht über eine nicht initialisierte lokale Variable auf statische Inhalte zugreifen?

Lesezeit: 5 Minuten

Benutzeravatar von Pshemo
Pshemo

Schauen Sie sich den folgenden Code an:

class Foo{
    public static int x = 1;
}

class Bar{    
    public static void main(String[] args) {
        Foo foo;
        System.out.println(foo.x); // Error: Variable 'foo' might not have been initialized
    }
}

Wie Sie sehen, während Sie versuchen, auf das statische Feld zuzugreifen x über ein nicht initialisiert lokale Variable Foo foo; Code foo.x generiert Kompilierungsfehler: Variable 'foo' might not have been initialized.

Es könnte erscheinen wie dieser Fehler macht Sinn, aber nur bis wir erkennen, dass der Zugriff auf a static Mitglied ist die JVM eigentlich nicht verwenden das Wert einer Variablen, sondern nur ihrer Typ.

Zum Beispiel kann ich initialisieren foo mit Wert null und dies ermöglicht uns den Zugriff x ohne Probleme:

Foo foo = null;
System.out.println(foo.x); //compiles and at runtime prints 1!!! 

Ein solches Szenario funktioniert, weil der Compiler das erkennt x ist statisch und behandelt foo.x als ob es so geschrieben wäre Foo.x (zumindest dachte ich das bisher).

Warum also Compiler plötzlich darauf besteht foo einen Wert haben die es NICHT verwenden wird überhaupt?


Haftungsausschluss: Dies ist kein Code, der in einer realen Anwendung verwendet werden würde, sondern ein interessantes Phänomen, auf das ich bei Stack Overflow keine Antwort finden konnte, also habe ich beschlossen, danach zu fragen.

  • Ich würde sagen, eine Einschränkung im Compiler, die es nicht wirklich wert ist, behoben zu werden, da der Code sowieso eine Warnung auslöst.

    – M.A

    16. September 2019 um 20:33 Uhr

  • @manouti Das ist auch meine Vermutung, aber ich interessiere mich immer noch dafür, warum sich der Compiler so verhält. Welcher Teil der Spezifikation erzwingt es?

    – Pschemo

    16. September 2019 um 20:35 Uhr

  • @portfoliobuilder Hier besteht, wie bereits erwähnt, im Falle eines Zugriffs kein NPE-Risiko static Member-Compiler nicht verwendet Wert variabel aber sein Typ. Wir können sogar schreiben ((Foo)null).x und das wird kompilieren und funktionieren weil der Compiler das erkennt x ist statisch (es sei denn, ich habe Ihren Kommentar falsch verstanden).

    – Pschemo

    16. September 2019 um 20:39 Uhr


  • Zugriff auf eine statische Variable aus einem nicht statischen Kontext (z foo.x) sollte ein Compilerfehler gewesen sein, als Java zum ersten Mal erstellt wurde. Leider segelte dieses Schiff vor über 25 Jahren und wäre eine bahnbrechende Veränderung, wenn sie es jetzt ändern würden.

    – Machtherr

    16. September 2019 um 20:49 Uhr


  • @portfoliobuilder “..es gibt absolut ein Risiko” Welches Risiko haben Sie im Sinn? Auch ein Nitpick: Beide Wege sind technisch Korrekt (leider), aber Foo.x ist bevorzugt (weshalb wir normalerweise Compilation bekommen Warnung wenn wir versuchen, eine Variante zu verwenden foo.x).

    – Pschemo

    16. September 2019 um 21:12 Uhr

Benutzeravatar von Nexevis
Nexevis

§15.11. Feldzugriffsausdrücke:

Wenn das Feld ist statisch:

Der primäre Ausdruck wird ausgewertet und das Ergebnis verworfen. Wenn die Auswertung des primären Ausdrucks abrupt abgeschlossen wird, wird der Feldzugriffsausdruck aus demselben Grund abrupt abgeschlossen.

Wo früher steht, dass der Feldzugriff durch identifiziert wird Primary.Identifier.

Dies zeigt, dass, obwohl es den nicht zu verwenden scheint Primary, es wird trotzdem ausgewertet und das Ergebnis dann verworfen, weshalb es initialisiert werden muss. Dies kann einen Unterschied machen, wenn die Auswertung den Zugriff wie im Angebot angegeben stoppt.

BEARBEITEN:

Hier ist ein kurzes Beispiel, nur um visuell zu demonstrieren, dass die Primary wird ausgewertet, obwohl das Ergebnis verworfen wird:

class Foo {
    public static int x = 1;
    
    public static Foo dummyFoo() throws InterruptedException {
        Thread.sleep(5000);
        return null;
    }
    
    public static void main(String[] args) throws InterruptedException {
        System.out.println(dummyFoo().x);
        System.out.println(Foo.x);
    }
}

Hier sieht man das dummyFoo() wird immer noch ausgewertet, weil die print wird um die 5 Sekunden verzögert Thread.sleep() obwohl es immer a zurückgibt null Wert, der verworfen wird.

Wenn der Ausdruck nicht ausgewertet wurde, wird die print würde sofort erscheinen, was bei der Klasse zu sehen ist Foo wird direkt für den Zugriff verwendet x mit Foo.x.

Notiz: Der Methodenaufruf wird auch als a betrachtet Primary gezeigt in §15.8 Primäre Ausdrücke.

  • Interessant, javac tut dies buchstäblich und generiert dabei eine Lade- und Pop-Anweisung ecj erzwingt die formale Regel, dh erlaubt keinen Zugriff über eine nicht initialisierte Variable, generiert aber keinen Code für die nebenwirkungsfreie Operation.

    – Holger

    17. September 2019 um 13:32 Uhr


Benutzeravatar von Andrew Tobilko
Andreas Tobilko

Kapitel 16. Eindeutiger Auftrag

Jede lokale Variable (§14.4) und jedes leere letzte Feld (§4.12.4, §8.3.1.2) muss einen definitiv zugewiesenen Wert haben, wenn ein Zugriff auf seinen Wert stattfindet.

Es spielt keine Rolle was Sie versuchen, über eine lokale Variable zuzugreifen. Die Regel ist, dass es vorher definitiv vergeben werden sollte.

Zu bewerten ein Feldzugriffsausdruck foo.xdas primary ein Teil davon (foo) müssen zuerst ausgewertet werden. Es bedeutet, dass der Zugriff auf foo auftreten, was zu einem Kompilierzeitfehler führt.

Bei jedem Zugriff auf eine lokale Variable oder ein leeres Endfeld x muss x vor dem Zugriff eindeutig zugewiesen werden, oder ein Kompilierzeitfehler auftritt.

Benutzeravatar von racraman
racraman

Es ist sinnvoll, die Regeln so einfach wie möglich zu halten, und „keine Variable verwenden, die möglicherweise nicht initialisiert wurde“ ist so einfach wie es nur geht.

Genauer gesagt gibt es eine etablierte Methode zum Aufrufen statischer Methoden – verwenden Sie immer den Klassennamen, keine Variable.

System.out.println(Foo.x);

Die Variable „foo“ ist ein unerwünschter Overhead, der entfernt werden sollte, und die Compiler-Fehler und -Warnungen könnten als dazu beitragend angesehen werden.

Andere Antworten erklären perfekt den Mechanismus hinter dem, was passiert. Vielleicht wollten Sie auch die Begründung hinter der Spezifikation von Java. Da ich kein Java-Experte bin, kann ich Ihnen die ursprünglichen Gründe nicht nennen, aber lassen Sie mich darauf hinweisen:

  • Jedes Stück Code hat entweder eine Bedeutung oder es löst einen Kompilierungsfehler aus.
  • (Für Statik, da eine Instanz unnötig ist, Foo.x ist selbstverständlich.)
  • Nun, was sollen wir damit machen foo.x (Zugriff über Instanzvariable)?
    • Es könnte ein Kompilierungsfehler sein, wie in C#, oder
    • Es hat eine Bedeutung. Da Foo.x bedeutet schon “einfach zugreifen x“, ist es vernünftig, dass die Ausdruck foo.x hat eine andere Bedeutung; das ist, jeder Teil des Ausdrucks ist gültig und zugreifen x.

Hoffen wir, dass jemand, der sich auskennt, den wahren Grund nennen kann. 🙂

1424550cookie-checkWarum können wir nicht über eine nicht initialisierte lokale Variable auf statische Inhalte zugreifen?

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

Privacy policy