Dlaczego nie możemy mieć metody statycznej w niestatycznej klasie wewnętrznej?
Jeśli uczynię wewnętrzną klasę statyczną, to działa. Czemu?
java
class
static
inner-classes
Rahul Garg
źródło
źródło
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.
źródło
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óbtest()
:W przypadku statycznej klasy wewnętrznej
Inner
można uzyskać dostęp do jej metody statycznej w następujący sposóbinnerTest()
:Jeśli jednak
Inner
nie jest statyczny, nie ma teraz czysto statycznego sposobu odniesienia się do metodyinnertest
. 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 doOuter.Inner.CONSTANT
jest gwarantowane jako jednoznaczne w sposób, w jakiOuter.Inner.staticFunction();
nie jest to wywołanie funkcji . Powiedzmy, że maszInner.staticFunction()
wywołaniagetState()
, które są zdefiniowane wOuter
. 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.
źródło
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ępOuter.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
iOuter.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 utworzonoInner
wystąpienie. Wszystkie odniesieniaOuter.staticMethod()
mają ten sam dokładny stan. Wszystkie odniesieniaOuter.Inner.CONSTANT
mają ten sam dokładny stan. Jednak odwołania doOuter.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ąpieniuInner
. 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ę:
Kiedy go kompilujesz, wygenerowany kod bajtowy będzie wyglądał tak, jakbyś napisał coś takiego:
Teraz, gdybyśmy zezwolili na statyczne metody w niestatycznych klasach wewnętrznych, możesz chcieć zrobić coś takiego.
... co wygląda dość rozsądnie, ponieważ
Inner
klasa wydaje się mieć jedną inkarnację naOuter
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:... 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).źródło
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 programuOuter
, nadal można użyć poleceniaOuter.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ć jeOuter
jako statyczne elementy członkowskie. Nie jest to gorsze niż użycie statycznych elementów członkowskich wInner
, gdzie i takOuter
można uzyskać dostęp do dowolnego prywatnego członkaInner
(nie poprawia hermetyzacji).Jeśli chcesz udostępnić coś we wszystkich instancjach
Inner
utworzonych przez jedenouter
obiekt, bardziej sensowne jest umieszczenie ich wOuter
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.
źródło
Od: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
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.
źródło
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ć:
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:
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:
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:
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.
źródło
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.
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.
źródło
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.
źródło
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.
źródło
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ę.
źródło
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,
można napisać w ten sposób
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.
źródło
Spróbuj potraktować klasę jako normalne pole, wtedy zrozumiesz.
źródło
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 ....
źródło
Dozwolone są statyczne metody w statycznych klasach zagnieżdżonych. Na przykład
źródło