To pytanie dotyczy tego, czy uczynić zagnieżdżoną klasę w Javie klasą zagnieżdżoną statycznie czy wewnętrznie zagnieżdżoną. Szukałem tutaj i nad przepełnieniem stosu, ale tak naprawdę nie mogłem znaleźć żadnych pytań dotyczących konsekwencji projektowych tej decyzji.
Znalezione przeze mnie pytania dotyczą różnicy między statycznymi i wewnętrznymi klasami zagnieżdżonymi, co jest dla mnie jasne. Jednak nie znalazłem jeszcze przekonującego powodu, aby kiedykolwiek używać statycznej zagnieżdżonej klasy w Javie - z wyjątkiem klas anonimowych, których nie rozważam w przypadku tego pytania.
Oto moje rozumienie efektu używania statycznych klas zagnieżdżonych:
- Mniej sprzężenia: generalnie uzyskujemy mniej sprzężenia, ponieważ klasa nie ma bezpośredniego dostępu do atrybutów swojej klasy zewnętrznej. Mniej sprzężenia oznacza ogólnie lepszą jakość kodu, łatwiejsze testowanie, refaktoryzację itp.
- Pojedynczy
Class
: moduł ładujący klasy nie musi dbać o nową klasę za każdym razem, gdy tworzymy obiekt klasy zewnętrznej. Ciągle otrzymujemy nowe obiekty dla tej samej klasy.
W przypadku klasy wewnętrznej ogólnie uważam, że ludzie uważają dostęp do atrybutów klasy zewnętrznej za profesjonalistę. Zaczynam się różnić pod tym względem z punktu widzenia projektowania, ponieważ ten bezpośredni dostęp oznacza, że mamy wysoki stopień sprzężenia, a jeśli kiedykolwiek chcemy wyodrębnić zagnieżdżoną klasę do oddzielnej klasy najwyższego poziomu, możemy to zrobić dopiero po zasadniczym odwróceniu w statyczną zagnieżdżoną klasę.
Moje pytanie sprowadza się zatem do tego: czy mylę się, zakładając, że dostęp do atrybutów dostępny dla niestatycznych klas wewnętrznych prowadzi do wysokiego sprzężenia, a tym samym do niższej jakości kodu, i wywnioskuję z tego, że (nieanonimowe) klasy zagnieżdżone powinny ogólnie być statycznym?
Innymi słowy: czy istnieje przekonujący powód, dla którego wolałaby zagnieżdżoną klasę wewnętrzną?
źródło
Odpowiedzi:
Joshua Bloch w punkcie 22 swojej książki „Effective Java Second Edition” mówi, kiedy użyć jakiego rodzaju klasy zagnieżdżonej i dlaczego. Poniżej kilka cytatów:
Jednym z powszechnych zastosowań statycznej klasy członka jest publiczna klasa pomocnicza, przydatna tylko w połączeniu z jej klasą zewnętrzną. Rozważmy na przykład wyliczenie opisujące operacje obsługiwane przez kalkulator. Wyliczenie operacji powinno być publiczną statyczną klasą członka
Calculator
klasy. KlienciCalculator
mogą wtedy odwoływać się do operacji przy użyciu nazw takich jakCalculator.Operation.PLUS
iCalculator.Operation.MINUS
.Jednym z powszechnych zastosowań niestatycznej klasy elementu jest zdefiniowanie adaptera, który pozwala na postrzeganie instancji klasy zewnętrznej jako instancji klasy niepowiązanej. Na przykład implementacje
Map
interfejsu zazwyczaj korzystają nonstatic klas członkowskie do wdrożenia ich poglądy zbiórki , które są zwracane przezMap
„skeySet
,entrySet
ivalues
metod. Podobnie implementacje interfejsów kolekcji, takie jakSet
iList
, zwykle używają niestatycznych klas elementów do implementacji swoich iteratorów:Jeśli deklarujesz klasę członkowską, która nie wymaga dostępu do załączającej instancji, zawsze umieszczaj
static
modyfikator w jej deklaracji, czyniąc ją statyczną, a nie niestatyczną klasą członkowską.źródło
MySet
instancji klasy zewnętrznej ? Mógłbym wyobrazić sobie coś w rodzaju typu zależnego od ścieżki, tj.myOneSet.iterator.getClass() != myOtherSet.iterator.getClass()
, Ale z drugiej strony w Javie nie jest to faktycznie możliwe, ponieważ klasa byłaby taka sama dla każdego.Masz rację zakładając, że dostęp do atrybutów dostępny dla niestatycznych klas wewnętrznych prowadzi do wysokiego sprzężenia, a tym samym do niższej jakości kodu, a (nieanonimowe i nielokalne ) klasy wewnętrzne powinny zasadniczo być statyczne.
Implikacje projektowe decyzji o uczynieniu klasy wewnętrznej niestatyczną są określone w Java Puzzlers , Puzzle 90 (pogrubiona czcionka w poniższym cytacie jest moja):
Jeśli jesteś zainteresowany, bardziej szczegółowa analiza Puzzle 90 znajduje się w tej odpowiedzi na Stack Overflow .
Warto zauważyć, że powyższa jest zasadniczo rozszerzoną wersją wskazówek zawartych w samouczku klas i obiektów Java :
Tak więc odpowiedź na pytanie, które zadałeś innymi słowami w samouczku, jest jedynym przekonującym powodem zastosowania niestatyczności jest, gdy wymagany jest dostęp do niepublicznych pól i metod instancji obejmującej .
Sformułowanie samouczka jest dość szerokie (może to być powód, dla którego Java Puzzlers próbują go wzmocnić i zawęzić). W szczególności bezpośredni dostęp do otaczających pól instancji nigdy nie był wymagany w moim doświadczeniu - w tym sensie, że alternatywne sposoby, takie jak przekazywanie ich jako parametrów konstruktora / metody, zawsze były łatwiejsze do debugowania i obsługi.
Ogólnie rzecz biorąc, moje (dość bolesne) spotkania z debugowaniem wewnętrznych klas bezpośrednio uzyskujących dostęp do pól otaczającej instancji wywarły silne wrażenie, że ta praktyka przypomina użycie stanu globalnego wraz ze znanymi złymi z nim związanymi.
Oczywiście Java sprawia, że uszkodzenie takiej „quasi-globalnej” jest zawarte w klasie zamkniętej, ale kiedy musiałem debugować konkretną klasę wewnętrzną, czułem, że taka pomoc zespołu nie pomogła zmniejszyć bólu: wciąż miałem pamiętać o „obcym” semantyce i szczegółach zamiast w pełni koncentrować się na analizie konkretnego kłopotliwego obiektu.
Dla zachowania kompletności mogą zdarzyć się przypadki, w których powyższe rozumowanie nie ma zastosowania. Na przykład, zgodnie z moim odczytaniem map.keySet javadocs , ta funkcja sugeruje ścisłe sprzężenie, w wyniku czego unieważnia argumenty przeciwko klasom niestatycznym:
Nie to, że powyższe w jakiś sposób ułatwiłoby utrzymanie, testowanie i debugowanie zaangażowanego kodu, ale przynajmniej pozwoliło by argumentować, że komplikacja pasuje / jest uzasadniona zamierzoną funkcjonalnością.
źródło
Niektóre nieporozumienia:
IIRC - Właściwie to może. (Oczywiście, jeśli są to pola obiektowe, nie będzie miał zewnętrznego obiektu do oglądania. Niemniej jednak, jeśli otrzyma taki obiekt z dowolnego miejsca, wówczas może uzyskać bezpośredni dostęp do tych pól).
Nie jestem do końca pewien, co masz na myśli. Program ładujący klasy jest zaangażowany tylko dwa razy, raz dla każdej klasy (gdy klasa jest załadowana).
Wędrowanie następuje:
W Javaland wydaje się, że trochę boimy się tworzyć klasy. Myślę, że to musi być znacząca koncepcja. Zazwyczaj należy do własnego pliku. Potrzebuje znacznej płyty grzewczej. Potrzebuje uwagi do swojego projektu.
W porównaniu z innymi językami - np. Scala, a może C ++: Nie ma absolutnie żadnego dramatu tworzącego malutki uchwyt (klasa, struktura, cokolwiek) w każdym momencie, gdy dwa bity danych należą do siebie. Elastyczne reguły dostępu pomagają ci, odcinając płytę kotłową, pozwalając ci fizycznie zbliżyć powiązany kod. Zmniejsza to „dystans umysłu” między dwiema klasami.
Możemy rozwiać wątpliwości projektowe dotyczące bezpośredniego dostępu, zachowując prywatność klasy wewnętrznej.
(... Gdybyś zapytał „dlaczego nie-statyczna publiczna klasa wewnętrzna?” To bardzo dobre pytanie - nie sądzę, żebym kiedykolwiek znalazł uzasadniony przypadek dla nich).
Możesz z nich korzystać w dowolnym momencie, co pomaga w odczytywaniu kodu i unikaniu naruszeń OSUSZANIA i płyty kotłowej. Nie mam gotowego przykładu, ponieważ z mojego doświadczenia nie zdarza się to często, ale się zdarza.
Statyczne klasy wewnętrzne są nadal dobrym podejściem domyślnym , btw. Niestatyczne ma generowane dodatkowe ukryte pole, przez które klasa wewnętrzna odnosi się do zewnętrznego.
źródło
Można go użyć do utworzenia wzoru fabrycznego. Wzorzec fabryczny jest często używany, gdy tworzone obiekty wymagają dodatkowych parametrów konstruktora do działania, ale są nużące, aby zapewnić za każdym razem:
Rozważać:
Użyty jako:
To samo można osiągnąć dzięki niestatycznej klasie wewnętrznej:
Użyj jako:
Fabryki skorzystają z konkretnie nazwanych metod, jak
create
,createFromSQL
lubcreateFromTemplate
. To samo można zrobić tutaj również w postaci wielu klas wewnętrznych:Potrzebne jest mniej przekazywania parametrów z klasy fabrycznej do skonstruowanej, ale czytelność może nieco ucierpieć.
Porównać:
vs
źródło