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!