Dlaczego nie możemy mieć metody statycznej w niestatycznej klasie wewnętrznej?
Jeśli uczynię wewnętrzną klasę statyczną, to działa. Czemu?
static
”)
Dlaczego nie możemy mieć metody statycznej w niestatycznej klasie wewnętrznej?
Jeśli uczynię wewnętrzną klasę statyczną, to działa. Czemu?
static
”)
Odpowiedzi:
Ponieważ wystąpienie klasy wewnętrznej jest niejawnie skojarzone z wystąpieniem swojej klasy zewnętrznej, nie może ono samodzielnie definiować żadnych metod statycznych. Ponieważ statyczna klasa zagnieżdżona nie może odwoływać się bezpośrednio do zmiennych instancji ani metod zdefiniowanych w otaczającej jej klasie, może ich używać tylko poprzez odwołanie do obiektu, dlatego można bezpiecznie zadeklarować metody statyczne w statycznej klasie zagnieżdżonej.
A.B.sync(X)
ani nawet (z poziomu A) B.sync(x)
?
Nie ma sensu zezwalanie na statyczną metodę w niestatycznej klasie wewnętrznej; jak możesz uzyskać do niego dostęp? Nie można uzyskać dostępu (przynajmniej początkowo) do niestatycznej instancji klasy wewnętrznej bez przechodzenia przez instancję klasy zewnętrznej. Nie ma czysto statycznego sposobu tworzenia niestatycznej klasy wewnętrznej.
W przypadku klasy zewnętrznej Outer
możesz uzyskać dostęp do metody statycznej w następujący sposób test()
:
Outer.test();
W przypadku statycznej klasy wewnętrznej Inner
można uzyskać dostęp do jej metody statycznej w następujący sposób innerTest()
:
Outer.Inner.innerTest();
Jeśli jednak Inner
nie jest statyczny, nie ma teraz czysto statycznego sposobu odniesienia się do metody innertest
. Niestatyczne klasy wewnętrzne są powiązane z konkretnym wystąpieniem ich klasy zewnętrznej. Funkcja różni się od stałej tym, że odniesienie do Outer.Inner.CONSTANT
jest gwarantowane jako jednoznaczne w sposób, w jaki Outer.Inner.staticFunction();
nie jest to wywołanie funkcji . Powiedzmy, że masz Inner.staticFunction()
wywołania getState()
, które są zdefiniowane w Outer
. Jeśli spróbujesz wywołać tę funkcję statyczną, masz teraz niejednoznaczne odwołanie do klasy Inner. To znaczy, na którym wystąpieniu klasy wewnętrznej wywołujesz funkcję statyczną? To ma znaczenie. Widzisz, nie ma prawdziwie statycznego sposobu odwoływania się do tej metody statycznej ze względu na niejawne odwołanie do obiektu zewnętrznego.
Paul Bellora ma rację, że projektanci języka mogli na to pozwolić. Musieliby wtedy ostrożnie zabronić dostępu do niejawnego odwołania do klasy zewnętrznej w statycznych metodach niestatycznej klasy wewnętrznej. W tym momencie, jaka jest wartość tego, że jest to klasa wewnętrzna, jeśli nie można odwoływać się do klasy zewnętrznej, chyba że statycznie? A jeśli dostęp statyczny jest w porządku, dlaczego nie zadeklarować statycznej całej klasy wewnętrznej? Jeśli po prostu uczynisz samą klasę wewnętrzną statyczną, nie masz niejawnego odniesienia do klasy zewnętrznej i nie masz już tej niejednoznaczności.
Jeśli faktycznie potrzebujesz statycznych metod na niestatycznej klasie wewnętrznej, prawdopodobnie musisz przemyśleć swój projekt.
Outer.Inner i = new Outer().new Inner();
Ponadto, klasy wewnętrzne są dozwolone zadeklarować statycznych stałych według JLS §15.28.
Outer.Inner.staticMethod()
tak, jak masz dostęp Outer.Inner.CONSTANT
. „Nie można uzyskać dostępu do ... niestatycznej instancji klasy wewnętrznej bez przechodzenia przez instancję klasy zewnętrznej”. Dlaczego potrzebujesz instancji? Nie potrzebujesz instancji, Outer
aby zadzwonić Outer.staticMethod()
. Wiem, że jest to trudne, ale chodzi mi o to, że formułowanie odpowiedzi w ten sposób nie ma sensu. IMHO projektanci języka mogliby na to pozwolić, gdyby chcieli.
Outer.Inner.CONSTANT
i Outer.Inner.staticMethod()
polega na tym, że odwołanie do stałej nie ma szans na niejawne odwołanie się do wystąpienia, Outer
w którym utworzono Inner
wystąpienie. Wszystkie odniesienia Outer.staticMethod()
mają ten sam dokładny stan. Wszystkie odniesienia Outer.Inner.CONSTANT
mają ten sam dokładny stan. Jednak odwołania do Outer.Inner.staticMethod()
są niejednoznaczne: stan „statyczny” nie jest w rzeczywistości statyczny, ze względu na niejawne odwołanie do klasy zewnętrznej w każdym wystąpieniu Inner
. Nie ma naprawdę jednoznacznego, statycznego sposobu dostępu do niego.
Outer.this
. Zgadzam się z projektantami języka Java, że nie ma powodu, aby zezwalać na statyczne metody lub nie ostateczne pola statyczne w klasach wewnętrznych, ponieważ wszystko w klasie wewnętrznej powinno znajdować się w kontekście klasy obejmującej.
Mam teorię, która może być poprawna lub nie.
Po pierwsze, powinieneś wiedzieć kilka rzeczy o tym, jak klasy wewnętrzne są implementowane w Javie. Załóżmy, że masz tę klasę:
class Outer {
private int foo = 0;
class Inner implements Runnable {
public void run(){ foo++; }
}
public Runnable newFooIncrementer(){ return new Inner(); }
}
Kiedy go kompilujesz, wygenerowany kod bajtowy będzie wyglądał tak, jakbyś napisał coś takiego:
class Outer {
private int foo = 0;
static class Inner implements Runnable {
private final Outer this$0;
public Inner(Outer outer){
this$0 = outer;
}
public void run(){ this$0.foo++; }
}
public Runnable newFooIncrementer(){ return new Inner(this); }
}
Teraz, gdybyśmy zezwolili na statyczne metody w niestatycznych klasach wewnętrznych, możesz chcieć zrobić coś takiego.
class Outer {
private int foo = 0;
class Inner {
public static void incrFoo(){ foo++; }
}
}
... co wygląda dość rozsądnie, ponieważ Inner
klasa wydaje się mieć jedną inkarnację na Outer
instancję. Ale jak widzieliśmy powyżej, niestatyczne klasy wewnętrzne w rzeczywistości są po prostu cukrem syntaktycznym dla statycznych klas „wewnętrznych”, więc ostatni przykład byłby w przybliżeniu równoważny z:
class Outer {
private int foo = 0;
static class Inner {
private final Outer this$0;
public Inner(Outer outer){
this$0 = outer;
}
public static void incrFoo(){ this$0.foo++; }
}
}
... co najwyraźniej nie zadziała, ponieważ this$0
nie jest statyczne. Ten rodzaj wyjaśnia, dlaczego metody statyczne nie są dozwolone (chociaż można argumentować, że można zezwolić na metody statyczne, o ile nie odwołują się do otaczającego obiektu) i dlaczego nie można mieć nieostatecznych pól statycznych ( byłoby sprzeczne z intuicją, gdyby instancje niestatycznych klas wewnętrznych z różnych obiektów miały wspólny „stan statyczny”). Wyjaśnia również, dlaczego końcowe pola są dozwolone (o ile nie odwołują się do otaczającego obiektu).
public static double sinDeg(double theta) { ... }
wewnętrzne zajęcia z narzędzi matematycznych?
Jedynym powodem jest to, że „nie trzeba”, więc po co go wspierać?
Składniowo nie ma powodu, aby zabraniać klasie wewnętrznej posiadania statycznych elementów członkowskich. Chociaż wystąpienie programu Inner
jest skojarzone z wystąpieniem programu Outer
, nadal można użyć polecenia Outer.Inner.myStatic
do statycznego elementu członkowskiego, Inner
jeśli zdecyduje się na to java.
Jeśli chcesz udostępnić coś we wszystkich instancjach Inner
, możesz po prostu umieścić je Outer
jako statyczne elementy członkowskie. Nie jest to gorsze niż użycie statycznych elementów członkowskich w Inner
, gdzie i tak Outer
można uzyskać dostęp do dowolnego prywatnego członka Inner
(nie poprawia hermetyzacji).
Jeśli chcesz udostępnić coś we wszystkich instancjach Inner
utworzonych przez jeden outer
obiekt, bardziej sensowne jest umieszczenie ich w Outer
klasie jako zwykłych członków.
Nie zgadzam się z opinią, że „statyczna klasa zagnieżdżona jest po prostu klasą najwyższego poziomu”. Myślę, że lepiej jest naprawdę traktować statyczną klasę zagnieżdżoną / klasę wewnętrzną jako część klasy zewnętrznej, ponieważ mają one dostęp do prywatnych członków klasy zewnętrznej. Członkowie klasy zewnętrznej są również „członkami klasy wewnętrznej”. Nie ma więc potrzeby obsługi statycznego elementu członkowskiego w klasie wewnętrznej. Wystarczy zwykły / statyczny element członkowski klasy zewnętrznej.
Od: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
Podobnie jak w przypadku metod i zmiennych instancji, klasa wewnętrzna jest powiązana z instancją swojej klasy otaczającej i ma bezpośredni dostęp do metod i pól tego obiektu. Ponadto, ponieważ klasa wewnętrzna jest skojarzona z instancją, nie może ona sama definiować żadnych statycznych elementów członkowskich.
Wyjaśnienie Oracle jest powierzchowne i zawzięte. Ponieważ nie ma technicznego ani składniowego powodu, aby wywłaszczać statyczne składowe w klasie wewnętrznej (jest to dozwolone w innych językach, takich jak C #), motywacją projektantów Javy był prawdopodobnie gust koncepcyjny i / lub kwestia wygody technicznej.
Oto moje spekulacje:
W przeciwieństwie do klas najwyższego poziomu, klasy wewnętrzne są zależne od instancji: instancja klasy wewnętrznej jest skojarzona z instancją każdej z jej klas zewnętrznych i ma bezpośredni dostęp do ich członków. To jest główna motywacja do posiadania ich w Javie. Wyrażony w inny sposób: klasa wewnętrzna jest przeznaczona do tworzenia instancji w kontekście instancji klasy zewnętrznej. Bez instancji klasy zewnętrznej klasa wewnętrzna nie powinna być bardziej użyteczna niż inne elementy instancji klasy zewnętrznej. Nazwijmy to zależnym od instancji duchem klas wewnętrznych.
Sama natura statycznych elementów członkowskich (które NIE są zorientowane obiektowo) koliduje z zależnym od instancji duchem klas wewnętrznych (który JEST zorientowany obiektowo), ponieważ można odwołać się / wywołać statyczny element członkowski klasy wewnętrznej bez wystąpienia klasy zewnętrznej przez przy użyciu kwalifikowanej nazwy klasy wewnętrznej.
Zwłaszcza zmienne statyczne mogą obrażać w jeszcze inny sposób: dwie instancje klasy wewnętrznej, które są powiązane z różnymi instancjami klasy zewnętrznej, miałyby wspólne zmienne statyczne. Ponieważ zmienne są składnikiem stanu, dwie instancje klas wewnętrznych w efekcie współdzieliłyby stan niezależnie od zewnętrznych instancji klas, z którymi są powiązane. Nie jest tak, że jest to niedopuszczalne, aby zmienne statyczne działały w ten sposób (akceptujemy je w Javie jako rozsądny kompromis dla czystości OOP), ale istnieje prawdopodobnie głębsza obraza, którą można popełnić, dopuszczając je w klasach wewnętrznych, których instancje są już połączone z instancjami klas zewnętrznych przez projekt. Zakazanie statycznym członkom w klasach wewnętrznych na korzyść ducha zależnego od instancji przynosi dodatkową korzyść w postaci uprzedzania tego głębszego przestępstwa OOP.
Z drugiej strony, żadne takie przestępstwo nie jest związane ze stałymi statycznymi, które nie konstytuują w znaczący sposób stanu, a więc są dopuszczalne. Dlaczego nie zabronić stałych statycznych dla maksymalnej spójności z duchem zależnym od instancji ? Być może dlatego, że stałe nie muszą zajmować więcej pamięci niż jest to konieczne (jeśli są zmuszane do tego, aby nie były statyczne, są kopiowane do każdej instancji klasy wewnętrznej, co jest potencjalnie marnotrawne). W przeciwnym razie nie potrafię sobie wyobrazić przyczyny wyjątku.
Może nie jest to solidne rozumowanie, ale IMO ma największy sens z pobieżnej uwagi Oracle w tej sprawie.
Krótka odpowiedź: Model mentalny większości programistów pokazujący, jak działa zakres, nie jest modelem używanym przez javac. Dopasowanie bardziej intuicyjnego modelu wymagałoby dużej zmiany w działaniu javac.
Głównym powodem, dla którego statyczne elementy składowe w klasach wewnętrznych są pożądane, jest czystość kodu - statyczny element członkowski używany tylko przez klasę wewnętrzną powinien mieszkać wewnątrz niej, a nie musi być umieszczony w klasie zewnętrznej. Rozważać:
class Outer {
int outID;
class Inner {
static int nextID;
int id = nextID++;
String getID() {
return outID + ":" + id;
}
}
}
Zastanów się, co się dzieje w getID (), gdy używam niekwalifikowanego identyfikatora „outID”. Zakres, w którym pojawia się ten identyfikator, wygląda mniej więcej tak:
Outer -> Inner -> getID()
Tutaj, ponownie, ponieważ tak właśnie działa javac, „Zewnętrzny” poziom zakresu obejmuje zarówno statyczne, jak i instancyjne elementy składowe Outer. Jest to mylące, ponieważ zwykle mówi się nam, aby myśleć o statycznej części klasy jako o kolejnym poziomie zakresu:
Outer static -> Outer instance -> instanceMethod()
\----> staticMethod()
W ten sposób, oczywiście, staticMethod () widzi tylko statyczne składowe Outer. Ale gdyby tak działał javac, to odwołanie się do zmiennej instancji w metodzie statycznej spowodowałoby błąd „nie można rozwiązać nazwy”. To, co naprawdę się dzieje, to fakt, że nazwa znajduje się w zasięgu, ale następnie włącza się dodatkowy poziom sprawdzania i dowiaduje się, że nazwa została zadeklarowana w kontekście instancji i odwołuje się do niej z kontekstu statycznego.
OK, jak to się ma do klas wewnętrznych? Naiwnie uważamy, że nie ma powodu, dla którego klasy wewnętrzne nie mogą mieć zakresu statycznego, ponieważ wyobrażamy sobie zakres działający w następujący sposób:
Outer static -> Outer instance -> Inner instance -> getID()
\------ Inner static ------^
Innymi słowy, statyczne deklaracje w klasie wewnętrznej i deklaracje instancji w klasie zewnętrznej są objęte zakresem w kontekście wystąpienia klasy wewnętrznej, ale żadna z nich nie jest w rzeczywistości zagnieżdżona w drugiej; zamiast tego oba są zagnieżdżone w statycznym zakresie Outer.
Po prostu nie działa javac - istnieje jeden poziom zasięgu zarówno dla elementów statycznych, jak i instancji, a zakres zawsze jest ściśle zagnieżdżony. Nawet dziedziczenie jest implementowane przez kopiowanie deklaracji do podklasy zamiast rozgałęziania i przeszukiwania zakresu nadklasy.
Aby obsługiwać statyczne elementy składowe klas wewnętrznych, javac musiałby albo podzielić zakresy statyczne i instancji oraz obsługiwać rozgałęzianie i ponowne łączenie hierarchii zakresów , albo musiałby rozszerzyć swoją prostą logiczną ideę „statycznego kontekstu”, aby zmienić, aby śledzić typ kontekstu na wszystkich poziomach klasy zagnieżdżonej w bieżącym zakresie.
Dlaczego nie możemy mieć metody statycznej w niestatycznej klasie wewnętrznej?
Uwaga: niestatyczna klasa zagnieżdżona jest nazywana klasą wewnętrzną, więc nie ma non-static inner class
takiej klasy.
Instancja klasy wewnętrznej nie istnieje bez odpowiedniego wystąpienia klasy zewnętrznej. Klasa wewnętrzna nie może deklarować statycznych elementów członkowskich innych niż stałe czasu kompilacji. Gdyby było to dozwolone, istniałaby niejednoznaczność co do znaczenia static
. W takim przypadku byłyby pewne niejasności:
Dlatego zapewne konstruktorzy zdecydowali się nie zajmować się tym problemem.
Jeśli uczynię wewnętrzną klasę statyczną, to działa. Czemu ?
Ponownie nie można uczynić klasy wewnętrznej statyczną, a raczej zadeklarować klasę statyczną jako zagnieżdżoną. W takim przypadku ta zagnieżdżona klasa jest w rzeczywistości częścią klasy zewnętrznej i może mieć statyczne elementy członkowskie bez żadnego problemu.
Temat ten zwrócił na siebie uwagę wielu, ale postaram się wyjaśnić w najprostszych słowach.
Po pierwsze, w odniesieniu do http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.1 , klasa lub interfejs jest inicjowany bezpośrednio przed pierwszym wystąpieniem / wywołaniem dowolnego elementu członkowskiego, który jest poprzedzony słowem kluczowym static.
Tak więc, jeśli pogodzimy się ze statycznym składnikiem w klasie wewnętrznej, doprowadzi to do zainicjowania klasy wewnętrznej, niekoniecznie klasy zewnętrznej / otaczającej. Tak więc utrudniamy sekwencję inicjalizacji klas.
Weź również pod uwagę fakt, że niestatyczna klasa wewnętrzna jest powiązana z wystąpieniem klasy otaczającej / zewnętrznej. Zatem skojarzenie z instancją będzie oznaczać, że klasa wewnętrzna będzie istnieć wewnątrz instancji klasy Outer i będzie się różnić między instancjami.
Upraszczając, aby uzyskać dostęp do statycznego elementu członkowskiego, potrzebujemy wystąpienia klasy Outer, z której ponownie będziemy musieli utworzyć wystąpienie niestatycznej klasy wewnętrznej. Statyczne elementy członkowskie nie powinny być powiązane z instancjami, dlatego pojawia się błąd kompilacji.
Klasa wewnętrzna to coś zupełnie innego niż statyczna klasa zagnieżdżona, chociaż obie mają podobną składnię. Statyczne klasy zagnieżdżone są tylko środkiem do grupowania, podczas gdy klasy wewnętrzne mają silne powiązanie - i dostęp do wszystkich wartości - ich klasy zewnętrznej. Powinieneś być pewien, dlaczego chcesz użyć klasy wewnętrznej, a wtedy powinno być całkiem naturalne, której z nich musisz użyć. Jeśli chcesz zadeklarować metodę statyczną, jest to prawdopodobnie statyczna klasa zagnieżdżona, którą i tak chcesz.
załóżmy, że istnieją dwa wystąpienia klasy zewnętrznej i oba mają instancję klasy wewnętrznej. Teraz, jeśli klasa wewnętrzna ma jeden statyczny element członkowski, zachowa tylko jedną kopię tego elementu w obszarze sterty. w tym przypadku oba obiekty klasy zewnętrznej będą odnosić się do this pojedyncza kopia i mogą ją zmienić razem. Może to spowodować sytuację "Brudnego odczytu", dlatego ta Java nie zastosowała tego ograniczenia. Innym mocnym punktem na poparcie tego argumentu jest to, że java dopuszcza tutaj ostateczne statyczne elementy członkowskie, których wartości nie mogą być zmieniono z dowolnego obiektu klasy zewnętrznej. Proszę, pozwól mi, jeśli się mylę.
Po pierwsze, dlaczego ktoś chce zdefiniować statyczny element członkowski w niestatycznej klasie wewnętrznej? odpowiedź brzmi: tak, że zewnętrzna składowa klasy może używać tych statycznych składowych tylko z wewnętrzną nazwą klasy, prawda?
Ale w tym przypadku możemy bezpośrednio zdefiniować element członkowski w klasie zewnętrznej. który będzie powiązany ze wszystkimi obiektami klasy wewnętrznej, wewnątrz instancji klasy zewnętrznej.
jak poniższy kod,
public class Outer {
class Inner {
public static void method() {
}
}
}
można napisać w ten sposób
public class Outer {
void method() {
}
class Inner {
}
}
Więc moim zdaniem, aby nie komplikować kodu, projektant java nie zezwala na tę funkcjonalność lub możemy zobaczyć tę funkcjonalność w przyszłych wersjach z kilkoma dodatkowymi funkcjami.
Spróbuj potraktować klasę jako normalne pole, wtedy zrozumiesz.
//something must be static. Suppose something is an inner class, then it has static keyword which means it's a static class
Outer.something
Nie ma sensu mieć statycznych członków klasy wewnętrznej, ponieważ w pierwszej kolejności nie będziesz mieć do nich dostępu.
Pomyśl o tym, aby uzyskać dostęp do statycznego elementu członkowskiego, którego używasz nazwa_klasy.memberName, w naszym przypadku powinno to być coś w rodzaju externalclassName.innerclassName.memberName ,,, teraz widzisz, dlaczego wewnętrzna klasa musi być statyczna ....