07 maja 2008

Blok inicjalizujący egzemplarz w Javie

W ostatnim wydaniu gazetki [JavaSpecialists] Issue 159 - The Law of Sudden Riches od dr Heinz M. Kabutza można było spotkać następujący kod:
 import java.util.concurrent.*;

public class BankAccountTest {
public static void main(String[] args) {
final BankAccount account = new BankAccount(1000);
for (int i = 0; i < 2; i++) {
new Thread() {
{
start();
}

public void run() {
while (true) {
account.deposit(100);
account.withdraw(100);
}
}
};
}
ScheduledExecutorService timer =
Executors.newSingleThreadScheduledExecutor();
timer.scheduleAtFixedRate(new Runnable() {
public void run() {
System.out.println(account.getBalance());
}
}, 1, 1, TimeUnit.SECONDS);
}
}
U "naszego" Wiktora Gworka natrafiłem na Poznajemy nowe języki: Scala, czyli jak wypisać elementy z listy, gdzie zaprezentował taki oto fragment kodu źródłowego:
 List lista = new ArrayList() {{
add(1); add(2); add(3); add(4);
}};
I dlaczego ja o tym?! Ciekawostką Javy jest sposób inicjalizacji klas. Jest kilka jej etapów, a tym, co łączy przytoczone przykłady dr Kabutza oraz Wiktora to użycie bloku inicjującego egzemplarz (8.6 Instance Initializers). W skrócie: blok egzemplarza wykonywany jest po wykonaniu konstruktora klas nadrzędnych, a przed wykonaniem konstruktora. Bardzo przydatna funkcjonalność, szczególnie w trakcie prototypowania. Wciąż o niej zapominam, więc teraz już zostanie w aktach.

Dodatkowo, jakbyśmy się zmówili z Mariuszem, który na swoim blogu umieścił wpis SCJP - Konstruktory i inicjalizacja. Jakby komukolwiek było mało wiedzy odnośnie tematu inicjowania klas, to może zapoznać się z nim u Mariusza. A warto, gdyż (potencjalnie) ma to ogromne znaczenie w kontenerach IoC/DI, gdzie inicjalizację klasy deleguje się do kontenera. Pierwszy raz natrafiłem na tę informację w książce Pro Wicket (Apress, 2006), gdzie autor umieścił ciekawy komentarz o unikatowej dla mnie wartości edukacyjnej, w którym wspomina właśnie o kolejności inicjalizacji pól klas pochodnych - DI and Field Initialization (strona 160). Streszczając komentarz, sprawa sprowadza się do zapamiętania, że wartości pól klas pochodnych zostaną w pełni zainicjowane (a tym samym nastąpi przypisanie wartości ich pól) po inicjalizacji klasy nadrzędnej i mimo przypisania im wartości przez kontener, ich docelowa wartość będzie tą, którą przypisaliśmy. Innymi słowy, jeśli w klasie nadrzędnej wywołamy "maszynerię" IoC/DI, która ustawi parametry klas pochodnych (tak się działo chociażby w wersji Wicket przed wersją 1.3, która jest tematem książki), to ta praca pójdzie do kosza przy ich faktycznej inicjalizacji (wartości zostaną nadpisane). Gdyby ktoś zechciał zilustrować problem w zgrabnym przykładzie z przyjemnością opublikowałbym go w kolejnym wpisie. Tak na prawdę, to nie wiem, czy taka sytuacja może w ogóle zajść, gdzie kontenery IoC (Spring, HiveMind, XBean, PicoContainer) tworzą egzemplarz za pomocą bezparametrowego konstruktora i wywołują metody set na nim lub po prostu wywołują konstruktor o zadanej licznie i typach parametrów. W takim przypadku ustanowienie domyślnej wartości polom egzemplarza nie ma znaczenia. Chyba, że się mylę? Wyprowdzi mnie ktoś z błędu?

Pytanie konkursowe: Gdzie użyto bloku inicjującego egzemplarz w kodzie dr Kabutza?

p.s. Jutro jestem na Uniwersytecie Śląskim w Sosnowcu, gdzie przedstawię produkty IBM w kontekście Korporacyjnej Javy 5 (pisałem o tym w IBM WAS 7.0 Open Beta oraz IBM RAD 7.5 Open Beta na moim wykładzie na Uniwersytecie Śląskim w Sosnowcu). Z pewnością nie zabraknie wielu przykładów z WAS7 i RAD7.5, których zauważyłem, że wiele dostarczono wraz z nimi. Już je zainstalowałem i nie mogę doczekać się, kiedy w końcu je zademonstruję w działaniu. Wyjeżdżam wcześniej, więc rozpoczynam jak zaplanowano o 11:30. Serdecznie zapraszam!

6 komentarzy:

  1. Ale się uśmiałem, jak przeczytałem "naszego" ;).

    A co do pytania to blok inicjalizacyjny znajduje się w klasie Thread (a w zasadzie w jej anonimowej podklasie), gdzie wątek sam siebie odpala. Przyznam, że takiego triku to jeszcze nie widziałem. Tworzę obiekt nowego wątku, a on sam startuje :).

    OdpowiedzUsuń
  2. Dla uzupełnienia dodam, że jeszcze także blok inicjalizacyjny klasy, który jest wywoływany tylko przy pierwszym odwołaniu do klasy. Wygląda dokładnie jak blok incjalizacyjny egzemplarza, z tym że poprzedzony jest słówkiem static. Zdarzyło mi się stosować go do inicjalizacji statycznych kolekcji, których potem nie modyfikuję a często wykorzystuję.

    OdpowiedzUsuń
  3. Witam

    niestety, ale w zdaniu: "W skrócie: blok egzemplarza wykonywany jest po utworzeniu egzemplarza danego typu (=zakończeniu wykonania konstruktora)"

    jest blad :)
    blok egzemplaza wykonywany jest po wykonaniu konstruktora klasy nadrzednej, "przed" wykonaniem konstruktora tz:

    w skroce:

    super();
    // teraz wykona sie blok
    System.out.println("dalsze wykonanie konstruktora");

    OdpowiedzUsuń
  4. Re Wiktor: od razu pomyślałem o Tobie kiedy zobaczyłem ten kod u Kabutza. No i nie mogłem oprzeć się, aby nie napisać "naszego", aby podkreślić, że te konstrukcje były już znane u nas dzięki Tobie! ;-)

    Re: Łukasz, pora zaprezentować jakiś kod dla rozpoczynających programowanie w Javie na...swoim blogu ;-) Widziałem, że coś tam pusto i aż prosi o nowy wpis. Idealna okazja, aby zacząć. Po 10-ciu wpisach, samo zacznie się pisać, a po 100-tu nie będziesz mógł skończyć (uwaga: uzależnia).

    Re: Borelli, wielkie dzięki za sprostowanie! Już poprawiłem i jak mogłeś zauważyć skorzystałem dokładnie z Twojego zdania. Wielkie, wielkie dzięki. Jestem Twoim dłużnikiem, bo dzisiaj nawet o tym rozmawiałem i nie do końca byłem pewien, czy dobrze napisałem na blogu.

    OdpowiedzUsuń
  5. Wiesz, ja sam nie czuję się wcale zaawansowanym programistą, ale mam w planach coś skrobnąć, na razie wrzuciłem notkę o ssh pod linuxem. Od czegoś trzeba zacząć, nie? ;)

    OdpowiedzUsuń
  6. Jeśli chodzi o formatowanie poleceń to zaznaczyłem je i wybrałem cytat blokowy, a następnie zmieniłem czcionkę na Courier. Oprócz tego usunąłem jedną linijkę w css-ach, bo przy cytatach blokowych pojawiał mi się obrazek, który nie zbyt mi pasował.

    OdpowiedzUsuń