Dlaczego Java zabrania pól statycznych w klasach wewnętrznych?

85
class OuterClass {
 class InnerClass {
  static int i = 100; // compile error
  static void f() { } // compile error
 }
} 

Chociaż nie jest możliwy dostęp do pola statycznego za pomocą OuterClass.InnerClass.i, jeśli chcę zarejestrować coś, co powinno być statyczne, np. Liczbę utworzonych obiektów InnerClass, pomocne byłoby uczynienie tego pola statycznym. Dlaczego więc Java zabrania statycznych pól / metod w klasach wewnętrznych?

EDYCJA: Wiem, jak sprawić, by kompilator był zadowolony ze statycznej klasy zagnieżdżonej (lub statycznej klasy wewnętrznej), ale chcę wiedzieć, dlaczego java zabrania statycznych pól / metod wewnątrz klas wewnętrznych (lub zwykłej klasy wewnętrznej) zarówno z projektu języka, jak i aspekty wdrożeniowe, jeśli ktoś wie o tym więcej.

Jichao
źródło
3
Moim ulubionym przykładem jest posiadanie Loggera tylko dla klasy wewnętrznej. Nie może być statyczny, jak wszystkie inne rejestratory.
Piotr Findeisen

Odpowiedzi:

32

Ideą klas wewnętrznych jest działanie w kontekście otaczającej instancji. W jakiś sposób zezwalanie na statyczne zmienne i metody zaprzecza tej motywacji?

8.1.2 Klasy wewnętrzne i instancje zamykające

Klasa wewnętrzna jest klasą zagnieżdżoną, która nie jest jawnie ani niejawnie zadeklarowana jako statyczna. Klasy wewnętrzne nie mogą deklarować statycznych inicjatorów (§8.7) ani interfejsów składowych. Klasy wewnętrzne nie mogą deklarować statycznych elementów członkowskich, chyba że są polami stałymi czasu kompilacji (§15.28).

Gregory Pakosz
źródło
18
Może to po prostu postanowił tak
Gregory Pakosz
3
nie możesz utworzyć instancji niestatycznego wewnętrznego bez odniesienia do rodzica, ale nadal możesz go zainicjować .
skaffman
Jeśli ClassLoaders przechowują pamięć podręczną z informacją „Klasa X została zainicjowana”, ich logika nie może być użyta do inicjowania wielu instancji klasy [obiekty reprezentujące] X (co jest potrzebne, gdy obiekty Class muszą być tworzone jako klasy wewnętrzne w kilku różne obiekty).
Erwin Smout
@skaffman To nadal nie ma sensu. Statyczne właściwości klasy wewnętrznej zostaną zainicjowane tylko raz, więc jaki byłby problem? W tej chwili mam statyczny hashmap i mam około 4 metod, które manipulują tylko tą mapą, dzięki czemu bardziej idealne jest grupowanie wszystkiego w klasie wewnętrznej. Jednak statyczny hasz musiałby teraz żyć na zewnątrz i być może z innymi powiązanymi rzeczami, co jest po prostu głupie. Jaki byłby problem z zainicjowaniem właściwości statycznych?
mmm
54

chcę wiedzieć, dlaczego java zabrania statycznych pól / metod wewnątrz klas wewnętrznych

Ponieważ te klasy wewnętrzne są klasami wewnętrznymi „instancji”. Oznacza to, że są one jak atrybut instancji otaczającego obiektu.

Ponieważ są to klasy „instancji”, nie ma sensu zezwalać na staticfunkcje, ponieważ staticma działać bez instancji.

To tak, jakbyś próbował jednocześnie utworzyć atrybut static / instance.

Weźmy następujący przykład:

class Employee {
    public String name;
}

Jeśli utworzysz dwie instancje pracownika:

Employee a = new Employee(); 
a.name = "Oscar";

Employee b = new Employee();
b.name = "jcyang";

Oczywiste jest, dlaczego każdy ma swoją wartość dla nieruchomości name, prawda?

To samo dzieje się z klasą wewnętrzną; każda instancja klasy wewnętrznej jest niezależna od drugiej instancji klasy wewnętrznej.

Więc jeśli spróbujesz utworzyć counteratrybut klasy, nie ma możliwości udostępnienia tej wartości w dwóch różnych instancjach.

class Employee {
    public String name;
    class InnerData {
        static count; // ??? count of which ? a or b? 
     }
}

Podczas tworzenia instancji aoraz bw powyższym przykładzie, co byłoby poprawne wartości dla zmiennej statycznej count? Nie można tego określić, ponieważ istnienie InnerDataklasy zależy całkowicie od każdego z otaczających obiektów.

Dlatego, gdy klasa jest zadeklarowana jako static, nie potrzebuje już żywej instancji, aby sama żyć. Teraz, gdy nie ma żadnej zależności, możesz swobodnie zadeklarować atrybut statyczny.

Wydaje mi się, że brzmi to powtarzalnie, ale jeśli pomyślisz o różnicach między atrybutami instancji a atrybutami klas, będzie to miało sens.

OscarRyz
źródło
4
Kupię twoje wyjaśnienie statycznych właściwości klasy wewnętrznej, ale jak @skaffman wskazuje w komentarzu do mojej odpowiedzi, co z metodami statycznymi ? Wydaje się, że metody powinny być dozwolone bez nakazu oddzielania ich od jakiejkolwiek instancji. Rzeczywiście, w Javie możesz wywoływać statyczne metody na instancjach (chociaż jest to uważane za zły styl). BTW: Poprosiłem kolegę próba kompilacji kodu PO jako C # i to robi kompilacji. Więc C # najwyraźniej na to pozwala, co pokazuje, że to, co chce zrobić OP, nie narusza jakiejś podstawowej zasady OO.
Asaph
2
Dokładnie tak samo dzieje się z metodami. Nie chodzi tutaj o to, czy atrybuty czy metody są statyczne, czy nie, ale o fakt, że klasa wewnętrzna jest instancją „materiałem”. Mam na myśli, problem polega na tym, że instancja takiej klasy wewnętrznej nie istnieje, dopóki nie zostanie utworzona klasa zewnętrzna. Zatem jaka metoda zostałaby wysłana, gdyby nie było nic. Uderzyłbyś w powietrze tylko dlatego, że będziesz potrzebować instancji na pierwszym miejscu.
OscarRyz,
1
O C # ... cóż. To nie jest OO ważne tylko dlatego, że C # na to pozwala, nie mam na myśli, że jest złe, ale C # zawiera kilka paradygmatów, które ułatwiają programowanie nawet kosztem spójności (musisz uczyć się nowych rzeczy z każdym wydaniem. pozwala między innymi na to i inne rzeczy. Myślę, że to dobra rzecz. Jeśli społeczność uważa, że ​​dodatkowa funkcja jest wystarczająco fajna, C # może ją mieć w przyszłości.
OscarRyz,
2
@OscarRyz Dlaczego potrzebujesz wystąpień klasy wewnętrznej, aby używać jej statycznych metod / pól? A przykładem [użytecznej] metody statycznej w klasie wewnętrznej jest prywatna metoda pomocnicza.
Leonid Semyonov
1
W przypadku użycia finalpól statycznych w klasie wewnętrznej w Javie jest dozwolone. Jak wyjaśnisz ten scenariusz?
Number945
34

InnerClassnie może mieć staticczłonków, ponieważ należy do instancji (z OuterClass). Jeśli zadeklarujesz InnerClassjak staticodłączyć go od instancji, Twój kod zostanie skompilowany.

class OuterClass {
    static class InnerClass {
        static int i = 100; // no compile error
        static void f() { } // no compile error
    }
}

BTW: nadal będziesz mógł tworzyć wystąpienia InnerClass. staticw tym kontekście pozwala na to bez otaczającej instancji OuterClass.

Asaf
źródło
6
InnerClassnie nie należą do OuterClass, przypadki z nim zrobić. Same dwie klasy nie mają takiego związku. Pytanie, dlaczego nie można mieć metod statycznych, InnerClasswciąż jest aktualne.
skaffman
9

W rzeczywistości możesz zadeklarować pola statyczne, jeśli są stałymi i zostały zapisane w czasie kompilacji.

class OuterClass {
    void foo() {
        class Inner{
            static final int a = 5; // fine
            static final String s = "hello"; // fine
            static final Object o = new Object(); // compile error, because cannot be written during compilation
        }
    }
}
vmolchanov
źródło
8
  1. class Sekwencja inicjalizacji jest krytycznym powodem.

Ponieważ klasy wewnętrzne są zależne od wystąpienia klasy otaczającej / Outer, dlatego klasa Outer musi zostać zainicjowana przed inicjalizacją klasy Inner.
To jest JLS mówi o inicjalizacji klas. Chodzi nam o to, że klasa T zostanie zainicjowana, jeśli

  • Używane jest pole statyczne zadeklarowane przez T, a pole to nie jest zmienną stałą.

Więc jeśli klasa wewnętrzna ma dostęp do pola statycznego, co spowoduje zainicjowanie klasy wewnętrznej, ale nie zapewni, że klasa otaczająca zostanie zainicjowana.

  1. Naruszyłoby to kilka podstawowych zasad . możesz przejść do ostatniej sekcji (do two cases), aby uniknąć rzeczy noobów

Jedną z rzeczy jest to , że gdy niektórzy będą zachowywać się tak jak normalna klasa pod każdym względem i jest to związane z klasą Outer.static nested classnested classstatic

Ale pojęcie Inner class/ czy to będzie związane z klasą zewnętrzną / otaczającą. Uwaga związana z instancją, a nie klasą. Teraz skojarzenie z instancją wyraźnie oznacza, że ​​( z koncepcji zmiennej instancji ) będzie istnieć wewnątrz instancji i będzie się różnić między instancjami. non-static nested classinstance

Teraz, kiedy zrobimy coś statycznego, spodziewamy się, że zostanie on zainicjowany podczas ładowania klasy i powinien być udostępniony wszystkim instancjom. Ale ze względu na to, że są niestatyczne, nawet same klasy wewnętrzne (na razie możesz zdecydowanie zapomnieć o wystąpieniu klasy wewnętrznej ) nie są współdzielone z wszystkimi wystąpieniami klasy zewnętrznej / otaczającej ( przynajmniej koncepcyjnie ), więc jak możemy się spodziewać, że jakaś zmienna klasy wewnętrznej zostaną podzielone między wszystkie wystąpienia klasy wewnętrznej.

Więc jeśli Java pozwala nam używać statycznej zmiennej wewnątrz nie statycznej klasy zagnieżdżonej. będą dwa przypadki .

  • Jeśli jest współdzielony ze wszystkimi instancjami klasy wewnętrznej, naruszy to pojęcie context of instance(zmienna instancji). W takim razie NIE.
  • Jeśli nie zostanie udostępniony wszystkim instancjom, narusza koncepcję statyczności. Znowu NIE.
Saif
źródło
5

Oto motywacja, którą uważam za najbardziej odpowiednią dla tego „ograniczenia”: Możesz zaimplementować zachowanie statycznego pola klasy wewnętrznej jako pole instancji obiektu zewnętrznego; Więc nie trzeba statycznych pól / metod . Chodzi mi o to, że wszystkie instancje klasy wewnętrznej jakiegoś obiektu współużytkują pole (lub metodę).

Więc załóżmy, że chcesz policzyć wszystkie instancje klasy wewnętrznej, zrobiłbyś:

public class Outer{
    int nofInner; //this will count the inner class 
                  //instances of this (Outer)object
                  //(you know, they "belong" to an object)
    static int totalNofInner; //this will count all 
                              //inner class instances of all Outer objects
    class Inner {
        public Inner(){
            nofInner++;
            totalNofInner++;
        }
    }
}
ianos
źródło
2
Ale pytanie brzmi: jaki jest powód dopuszczenia pól statycznych, kiedy są finalwtedy deklarowane ?
Solace
jeśli spojrzysz na [ stackoverflow.com/a/1954119/1532220] (odpowiedź OscarRyzs powyżej): Jego motywacją jest to, że wartość nie może być powiązana ze zmienną. Oczywiście, jeśli zmienna jest ostateczna, możesz dość łatwo wiedzieć, jaką wartość przypisać (musisz wiedzieć).
ianos
2

W prostych słowach, niestatyczne klasy wewnętrzne są zmiennymi instancji dla klasy zewnętrznej i są tworzone tylko wtedy, gdy tworzona jest klasa zewnętrzna, a obiekt klasy zewnętrznej jest tworzony w czasie wykonywania, podczas gdy zmienne statyczne są tworzone w czasie ładowania klasy. Tak więc niestatyczna klasa wewnętrzna jest elementem środowiska uruchomieniowego, dlatego statyczna nie jest częścią niestatycznej klasy wewnętrznej.

UWAGA: traktuj klasy wewnętrzne zawsze jak zmienną dla klasy zewnętrznej, mogą one być statyczne lub niestatyczne, jak wszystkie inne zmienne.

Mannu
źródło
Ale klasa wewnętrzna może mieć static finalstałe.
Deszcz
1

Ponieważ spowodowałoby to niejednoznaczność w znaczeniu „statyczności”.

Klasy wewnętrzne nie mogą deklarować statycznych elementów członkowskich innych niż stałe czasu kompilacji. Byłaby dwuznaczność co do znaczenia słowa „statyczny”. Czy to oznacza, że ​​w maszynie wirtualnej jest tylko jedno wystąpienie? Czy tylko jedna instancja na obiekt zewnętrzny? Projektanci języka postanowili nie zajmować się tym problemem.

Zaczerpnięte z „Core Java SE 9 for the Impatient” autorstwa Cay S. Horstmanna. Str. 90 Rozdział 2.6.3

aj hrishikesh
źródło
-1

Myślę, że to dla spójności. Chociaż wydaje się, że nie ma dla niego żadnych ograniczeń technicznych, nie byłbyś w stanie uzyskać dostępu do statycznych elementów klasy wewnętrznej z zewnątrz, tj. OuterClass.InnerClass.iPonieważ środkowy krok nie jest statyczny.

Chochos
źródło
Ale klasa wewnętrzna może mieć static finalstałe.
Deszcz