2013-06-22 13 views
16

Wenn eine lokale innere Klasse innerhalb einer Methode deklariert wird, warum ist es zulässig, endgültige statische Strings oder Ints einzuschließen, aber nicht zulässig, andere Objekte einzuschließen?Java finale statische Deklarationen in lokalen Methodenklassen

Zum Beispiel:

class Outer { 
void aMethod() { 
    class Inner { 
     final static String name = "compiles"; 
     final static int ctr = 10; // compiles 
     final static Integer intThree = Integer.valueOf(3); // does not compile! 
     final static obj objConst = new Object(); // does not compile! 
    } 

    Inner inner = new Inner(); 
} 
} 

Wenn ich dies kompilieren, erhalte ich die folgende:

InnerExample.java:6: inner classes cannot have static declarations 
     final static Integer outer = Integer.valueOf(3); 
          ^
InnerExample.java:7: inner classes cannot have static declarations 
     final static Object objConst = new Object(); 
          ^

Warum der Unterschied? Liegt es daran, dass String unveränderlich ist? Wenn ja, wäre nicht Integer.valueOf() auch gültig?

+3

Ich bin mir ziemlich sicher, es ist, weil "compiles" und 10 Konstanten Ausdrücke für die Kompilierung sind, aber ich habe die JLS Regel noch nicht gefunden. –

Antwort

17

Dies liegt daran, dass die ersten beiden statischen Elemente den Kompilierzeitkonstanten vom primitiven Typ oder vom Typ String zugewiesen sind.

Vom Java Language Specification, section 8.1.3:

8.1.3. Innere Klassen und umschließende Instanzen

Innere Klassen können statische Member nicht deklarieren, es sei denn, sie sind konstante Variablen (§4.12.4) oder es tritt ein Fehler bei der Kompilierung auf.

Und von 4.12.4:

Eine Variable vom Urtyp oder Typ String, das ist endgültig und mit einem Kompilierung-konstanten Ausdruck initialisiert (§15.28) wird eine konstante Variable genannt.

EDIT:

fand ich diese zunächst überraschend. Wenn Sie darüber nachdenken, besteht ein Vorteil dieser Einschränkung darin, dass Sie sich keine Gedanken darüber machen müssen, wann statische Elemente innerer Klassen initialisiert werden. Sie können eine innere Klasse in ihrer enthaltenen Klasse verschieben, ohne sich darum zu kümmern, dass die Werte ihrer statischen Elemente geändert werden.

3

Betrachten wir die Definition eines Kompilierung-Konstante Ausdruck von 15.28:

Ein Kompilierung-Konstante Ausdruck ist ein Ausdruck einen Wert von primitiven Typ oder einen String angibt, die nicht abrupt nicht vervollständigen und zusammengesetzt ist, die nur unter Verwendung von folgendem:

  • Literale Urtyp und Literale vom Typ String (§3.10.1, §3.10.2, §3.10.3, §3.10.4, §3.10.5)
  • Casts auf primitive Typen und wirft String (§15.16)
  • Die unäre Operatoren +, -, ~ und ! (aber nicht ++ oder --) (§15.15.3, §15.15.4, eintippen §15.15.5, §15.15.6)
  • Die multiplikativen Operatoren *, / und % (§15.17)
  • Die additiven Operatoren + und - (§15.18)
  • Die Verschiebungsoperatoren <<, >> und >>> (§15.19)
  • Die relationalen Operatoren <, <=, > und >= (aber nicht instanceof) (§15.20)
  • Die Gleichheitsoperatoren == und != (§15.21)
  • Die bitweise und logische Operatoren &, 012.und | (§15.22)
  • Der bedingte-und Betreiber && und der bedingte oder Betreiber || (§15.23, §15.24)
  • Der ternäre Konditionaloperator ? : (§15.25)
  • Klammerausdrücken (§ 15.8.5) dessen Ausdruck ein konstanter Ausdruck ist.
  • Einfache Namen (§6.5.6.1), die sich auf konstante Variablen beziehen (§4.12.4).
  • Qualifizierte Namen (§6.5.6.2) des Formulars TypeName. Bezeichner, die sich auf konstante Variablen beziehen (§4.12.4).

von der Definition eines konstanten Ausdrucks Compile-Zeit Nach, haben wir 4.12.4:

Eine Variable vom Urtyp oder String geben, das heißt final und mit einem compile- initialisiert Zeitkonstante Ausdruck (§15.28), heißt eine konstante Variable. Schließlich

, von 8.1.3:

Innere Klassen können nicht statisch Mitglieder erklären, es sei denn, sie konstante Variablen (§4.12.4) oder ein Fehler bei der Kompilierung auftritt.

5

Mehr zur vorherigen Antwort. Der zugewiesene Wert muss vom Compiler nachweisbar sein, dass es sich um eine Konstante handelt. Der Java-Compiler kennt die Semantik der Basistypen (int, float usw.) und der Klasse java.lang.String, aber keine anderen Klassen. Dies kann die Konstante der ersten beiden Beispiele verstehen.

Der Compiler versteht nicht, dass Integer.valueOf (3) ist auch (effektiv) eine Konstante (tatsächlich nicht konstant, sondern immer gleich) Wert, auch wenn ein Mensch, der, wie die Klasse Integer Werke kennt, weiß, dass . Der Compiler behandelt dies so, als wäre es Integer.valueOf (x), das sich ändern kann. Es wäre schön, wenn Java eine Annotation wie @interface Consistent anbietet, die das Verhalten der Methode für alle gegebenen Parameter als stabil erklärt, wie zum Beispiel:

Integer-Klasse: @Consistent public Integer valueOf (int x) { ...}

final static Ganzzahl intThree = Integer.valueOf (3); // jetzt kompiliert!

Das gibt an, dass die Methode bei jedem Aufruf mit den gleichen Argumentwerten entweder das gleiche oder ein gleiches Objekt zurückgibt. Da das Argument ein konstanter Ausdruck ist, kann der Compiler folgern, dass das Ergebnis in allen Anwendungen gleich/gleich ist und somit als Konstante behandelt werden kann. In diesem Fall gibt Integer das gleiche Objekt zurück, aber es kann ein anderes (aber gleiches) Objekt für viel größere Eingabewerte zurückgeben (dh es speichert Werte in der Nähe von 0).

Beachten Sie, dass "neu" immer ein anderes Objekt zurückgibt. Für ein neues Objekt() ist es immer ein Objekt, das keinem anderen Objekt gleicht.

+0

Dies ist eine fantastische Erklärung und hilft, den zweiten Teil zu beantworten, wie man die Operation von Integer.valueOf() in Bezug auf Kompilierzeitkonstanten versteht. –

Verwandte Themen