2013-06-22 28 views
16

Podczas deklarowania lokalnej klasy wewnętrznej w metodzie, dlaczego legalne jest dołączanie końcowych ciągów statycznych lub int, ale nie jest legalne, aby uwzględnić inne obiekty?Ostateczne deklaracje statyczne Java w metodzie lokalnych

Na przykład:

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

Kiedy skompilować tego, pojawia się następujący:

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

Dlaczego rozróżnienie? Czy to dlatego, że String jest niezmienny? Jeśli tak, to czy wartość Integer.valueOf() nie byłaby ważna?

+3

Jestem prawie pewny, że to dlatego, że "kompiluje", a 10 to stałe wyrażenia w czasie kompilacji, ale jeszcze nie znalazłem zasady JLS. –

Odpowiedz

17

Dzieje się tak, ponieważ pierwsze dwa statyczne elementy są przypisywane do stałych kompilacji typu pierwotnego lub typu String.

Z Java Language Specification, section 8.1.3:

8.1.3. Inner Classes and Enclosing Instances

Klasy wewnętrzne mogą nie zadeklarować elementów statycznych, chyba że są to zmienne stałe (§4.12.4) lub wystąpi błąd podczas kompilacji.

A z 4.12.4:

Zmienna pierwotnej typu lub typu ciąg, który jest ostateczny i inicjowane z kompilacji stałej ekspresji (§15.28), nazywany jest zmienna stała.

EDIT:

Znalazłem to zaskakujące na początku. Myśląc o tym więcej, jedną z korzyści tego ograniczenia jest to, że nie ma potrzeby martwić się, gdy statyczne elementy klas wewnętrznych zostaną zainicjowane. Możesz przenosić klasę wewnętrzną w swojej klasie zawierającej, bez obawy, że wartości jej statycznych członków zostaną zmienione.

3

Rozważmy definicję kompilacji stałej ekspresji od 15.28:

kompilacji czasu stała ekspresja jest wyrazem oznaczającym wartość prymitywnego typu lub ciąg znaków, który nie kończy się nagle i składa się z wykorzystaniem wyłącznie:

  • literały prymitywnego rodzaju i literałach typu String (§3.10.1, §3.10.2, §3.10.3, §3.10.4, §3.10.5)
  • odlewnictwa do prymitywnych typów i rzuca wpisać String (§15.16)
  • Operatorzy unarne +, -, ~ i ! (ale nie ++ lub --) (§15.15.3, §15.15.4, §15.15.5, §15.15.6)
  • Multiplikatywni operatorzy: *, / i % (§15.17)
  • Operatorzy dodatków + i - (§15.18)
  • operatory przesunięcia <<, >> i >>> (§15.19)
  • operatorów relacji <, <=, > i >= (ale nie instanceof) (§15.20)
  • operatorzy równości == i != (§15.21)
  • bitowego i operatory logiczne &,i | (§15.22)
  • Warunkowe, i operator && i warunkowego lub operator || (§15.23, §15.24)
  • Trójskładnikowy operator warunkowy ? : (§15.25)
  • wyrażenia w nawiasach (§ 15.8.5), którego zawarte wyrażenie jest stałym wyrażeniem.
  • Nazwy proste (§6.5.6.1), które odnoszą się do zmiennych stałych (§4.12.4).
  • Nazwy kwalifikowane (§6.5.6.2) formularza NazwaPunktu. Identyfikator odnoszący się do stałych zmiennych (§4.12.4).

wynikających z definicji kompilacji stałej ekspresji mamy 4.12.4:

zmienną pierwotnej typu lub typu String, to final i inicjowane z compile- wyrażenie czasu stałego (§ 15.28), jest nazywane zmienną stałą.

Wreszcie z 8.1.3:

Klasy wewnętrzne nie może zadeklarować elementy statyczne, chyba że są zmienne ciągłe (§4.12.4) lub kompilacji występuje błąd.

5

Więcej o poprzedniej odpowiedzi. Przypisana wartość musi zostać udowodniona przez kompilator, że jest stała. Kompilator Java zna semantykę typów podstawowych (int, float itp.) I klasy java.lang.String, ale nie innych klas. To może zrozumieć stałość dwóch pierwszych przykładów.

Kompilator nie rozumie, że wartość Integer.valueOf (3) jest również (efektywnie) stałą (właściwie nie stałą, ale zawsze taką samą) wartość, mimo że człowiek, który wie, jak działa klasa Integer, wie o tym . Kompilator traktuje to tak, jakby to była zmienna Integer.valueOf (x). Byłoby miło, gdyby Java oferowała adnotację, taką jak @interface Consistent, która zadeklarowała zachowanie metody jako stabilną dla dowolnych parametrów podanych, takich jak:

W klasie Integer: @ Spójna wartość Integer publicznaOff (int x) { ...}

końcowy statyczny Integer intThree = Integer.valueOf (3); // teraz kompiluje!

Wskazuje, że metoda zwraca ten sam lub równy obiekt przy każdym wywołaniu z tymi samymi wartościami argumentów. Ponieważ argument jest wyrażeniem stałym, kompilator może wywnioskować, że wynik będzie taki sam/równy we wszystkich zastosowaniach i dlatego może być traktowany jako stała. W tym przypadku Integer zwraca ten sam obiekt, ale może zwrócić inny (ale równy) obiekt dla wielu wartości wejściowych lagera (tzn. Buforuje wartości w pobliżu 0).

Należy pamiętać, że "nowy" zawsze zwraca inny obiekt. Dla nowego Object() jest to zawsze obiekt nie równy jakiemukolwiek innemu obiektowi.

+0

To jest fantastyczne wyjaśnienie i pomaga w odpowiedzi na drugą część, czyli jak zrozumieć działanie Integer.valueOf() w odniesieniu do stałych czasowych kompilacji. –