Ostatnio natknąłem się na konstrukcję Java, której nigdy wcześniej nie widziałem i zastanawiałem się, czy powinienem jej użyć. Wydaje się, że nazywa się to blokami inicjalizacyjnymi .
public class Test {
public Test() { /* first constructor */ }
public Test(String s) { /* second constructor */ }
// Non-static initializer block - copied into every constructor:
{
doStuff();
}
}
Blok kodu zostanie skopiowany do każdego konstruktora, tzn. Jeśli masz wiele konstruktorów, nie musisz przepisywać kodu.
Widzę jednak trzy główne wady wykorzystujące tę składnię:
- Jest to jeden z nielicznych przypadków w Javie, w których ważna jest kolejność kodu, ponieważ można zdefiniować wiele bloków kodu i będą one wykonywane w kolejności, w jakiej zostały zapisane. Wydaje mi się to szkodliwe, ponieważ zmiana kolejności bloków kodu spowoduje zmianę kodu.
- Używając go, nie widzę żadnych korzyści. W większości przypadków konstruktory będą wywoływać się z pewnymi predefiniowanymi wartościami. Nawet jeśli tak nie jest, kod można po prostu umieścić w prywatnej metodzie i wywołać z każdego konstruktora.
- Zmniejsza to czytelność, ponieważ można umieścić blok na końcu klasy, a konstruktor zwykle znajduje się na początku klasy. Spoglądanie na zupełnie inną część pliku kodu jest zupełnie sprzeczne z intuicją, jeśli nie spodziewasz się, że będzie to konieczne.
Jeśli moje powyższe stwierdzenia są prawdziwe, dlaczego (i kiedy) wprowadzono ten konstrukt językowy? Czy istnieją uzasadnione przypadki użycia?
{ doStuff(); }
na poziomie klasy jest blok inicjujący.doStuff()
Odpowiedzi:
Są dwa przypadki, w których używam bloków inicjalizujących.
Pierwszy służy do inicjowania członków końcowych. W Javie możesz zainicjować element końcowy albo zgodny z deklaracją, albo możesz zainicjować go w konstruktorze. W metodzie zabrania się przypisywania do końcowego członka.
Jest to ważne:
Jest to również ważne:
To jest nieprawidłowe:
Jeśli masz wiele konstruktorów i nie możesz zainicjować wbudowanego elementu końcowego (ponieważ logika inicjalizacji jest zbyt złożona) lub jeśli konstruktory nie mogą się wywołać, możesz skopiować / wkleić kod inicjujący lub użyć blok inicjalizujący.
Innym przypadkiem użycia, jaki mam dla bloków inicjalizacyjnych, jest budowanie małych struktur danych pomocniczych. Deklaruję element członkowski i umieszczam w nim wartości zaraz po jego deklaracjach we własnym bloku inicjującym.
źródło
squareVal = val * val
będą narzekać na dostęp do niezainicjowanych wartości. Bloki inicjalizujące nie mogą zależeć od argumentów przekazanych do konstruktora. Typowym rozwiązaniem tego rodzaju problemu jest zdefiniowanie jednego konstruktora „podstawowego” o złożonej logice i zdefiniowanie wszystkich innych konstruktorów pod tym względem. W rzeczywistości większość zastosowań inicjatorów instancji można zastąpić tym wzorcem.Zasadniczo nie używaj niestatycznych bloków inicjalizacyjnych (i być może unikaj również bloków statycznych).
Myląca składnia
Patrząc na to pytanie, istnieją 3 odpowiedzi, ale oszukałeś 4 osoby tą składnią. Byłem jednym z nich i pisałem w Javie od 16 lat! Oczywiście składnia jest potencjalnie podatna na błędy! Trzymałbym się od tego z daleka.
Konstruktory teleskopowe
W przypadku naprawdę prostych rzeczy można użyć konstruktorów „teleskopowych”, aby uniknąć tego zamieszania:
Wzór konstruktora
Jeśli potrzebujesz doStuff () na końcu każdego konstruktora lub innej zaawansowanej inicjalizacji, być może wzorzec konstruktora byłby najlepszy. Josh Bloch wymienia kilka powodów, dla których budowniczowie są dobrym pomysłem. Konstruktorzy poświęcają trochę czasu na pisanie, ale właściwie napisane, są przyjemnością w użyciu.
Statyczne pętle inicjujące
Często używałem statycznych inicjatorów, ale czasami wpadałem w pętle, w których 2 klasy zależały od siebie nawzajem bloków statycznego inicjatora, zanim klasa mogła zostać w pełni załadowana. Powodowało to „nie udało się załadować klasy” lub podobnie niejasny komunikat o błędzie. Musiałem porównać pliki z ostatnią znaną działającą wersją w kontroli źródła, aby dowiedzieć się, na czym polega problem. W ogóle nie ma zabawy.
Leniwa inicjalizacja
Może statyczne inicjalizatory są dobre ze względu na wydajność, gdy działają i nie są zbyt mylące. Ale ogólnie wolę obecnie leniwe inicjowanie niż statyczne inicjalizatory. Jest jasne, co robią, nie spotkałem się jeszcze z błędem ładowania klas i działają one w większej liczbie sytuacji inicjalizacyjnych niż w blokach inicjujących.
Definicja danych
Zamiast statycznej inicjalizacji do budowania struktur danych (porównaj z przykładami w innych odpowiedziach), teraz używam niezmiennych funkcji pomocniczych definicji danych Paguro :
Conculsion
Na początku Javy bloki inicjalizacyjne były jedynym sposobem na zrobienie pewnych rzeczy, ale teraz są mylące, podatne na błędy, aw większości przypadków zostały zastąpione lepszymi alternatywami (wyszczególnione powyżej). Interesujące jest wiedzieć o blokach inicjalizujących, na wypadek, gdy zobaczysz je w starszym kodzie lub pojawią się w teście, ale gdybym robił przegląd kodu i widziałem jeden w nowym kodzie, poprosiłbym cię o uzasadnienie, dlaczego żaden z powyższe alternatywy były odpowiednie przed podaniem kciuka do góry.
źródło
Oprócz inicjalizacji zmiennej instancji zadeklarowanej jako
final
(patrz odpowiedź barjaka ) wspomniałbym również ostatic
bloku inicjalizacji.Możesz użyć ich jako swego rodzaju „statycznego konstruktora”.
W ten sposób można wykonać złożone inicjalizacje zmiennej statycznej przy pierwszym odwołaniu do klasy.
Oto przykład zainspirowany przykładem Barjaka:
źródło
Jeśli chodzi o statyczne bloki inicjujące, ich podstawową funkcją jest działanie jako domyślny konstruktor w klasach anonimowych. To w zasadzie ich jedyne prawo do istnienia.
źródło
Całkowicie zgadzam się z instrukcjami 1, 2, 3. Z tych powodów nigdy nie używam inicjatorów blokowych i nie wiem, dlaczego istnieje w Javie.
Jestem jednak zmuszony użyć inicjatora bloku statycznego w jednym przypadku: gdy muszę utworzyć instancję pola statycznego, którego konstruktor może zgłosić sprawdzony wyjątek.
Ale zamiast tego musisz zrobić:
Uważam ten idiom za bardzo brzydki (to także zapobiega oznaczeniu
context
jakofinal
), ale jest to jedyny obsługiwany przez Javę sposób inicjowania takich pól.źródło
context = null;
blok catch, możesz określić kontekst jako ostateczny.The final field context may already have been assigned
static { JAXBContext tempCtx = null; try { tempCtx = JAXBContext.newInstance(Foo.class); } catch (JAXBException ignored) { ; } context = tempCtx; }