sobota, 3 maja 2008

Java Killers #004

Witam w kolejnym odcinku Java Killers. Odcinek ten pojawić się miał troszkę później (jest na tyle ciekawy, że chciałem z nim poczekać do połowy maja, aż wszyscy bez wyjątku wytrzeźwieją po weekendzie majowym), ale ponieważ pojawił się na jdn.pl jego preview w komentarzach do jednego z poprzednich JK, postanowiłem umieścić go teraz, aby pozostał jeszcze jakiś element zaskoczenia. A więc, aby jak zwykle nie przedłużać wstępów, zabierzmy się do dzieła.

Dziś porównamy sobie obiekty typu Integer, ot takie najzwyklejsze ==. Przejdźmy więc do kodu:


1 package javakillers.part004;
2
3 /**
4 *
5 * @author pawel
6 */
7 public class Main
8 {
9 public static void main(String[] args)
10 {
11 Main m = new Main();
12 m.run();
13 }
14 public void run()
15 {
16 check(2, 2);
17 check(2000, 2000);
18 }
19
20 void check(Integer a,Integer b)
21 {
22 System.out.print((a==b) + " ");
23 }
24 }
25
26



Proste prawda? No więc teraz pytanie, jaka będzie odpowiedź? Standardowo 4 możliwe odpowiedzi do wyboru:

A) true true
B) false false
C) false true
D) true false




Okazuje się, że prawidłowa odpowiedź to D) true false. Ale, że co? Jak to możliwe?? Ja rozumiem A lub B ale, że D? Żeby zrozumieć czemu odpowiedź jest taka a nie inna należy zajrzeć do specyfikacji, autoboxing opisany jest w JSR-201, gdzie w rozdziale piątym czytamy:

"If the value p being boxed is true, false, a byte, a char in the range \u0000 to \u007f, or an int or short number between -128 and 127, then let r1 and r2 be the results of any two boxing conversions of p. It is always the case that r1 == r2."

Wszystko rozchodzi się o zarządzanie pamięcią. Jeśli jakąś wartość prymitywną możemy zmieścić w jednym bajcie (czyli wszystkie boolean, byte oraz char'y od \u0000 do \u007f oraz short i int w przedziale -128 do 127), wtedy boxowane obiekty znajdują się w jednym miejscu pamięci, a więc sprawdzanie ich referencji da nam zawsze true.

Miażdzące prawda?

Pytanie teraz, jak się przed tym chronić? Zasada podstawowa nakazuje przede wszystkim sprawdzanie obiektów poprzez sprawdzanie ich zawartości (używając metody equals), a nie referencji. Takie podejście uratuje Wam nie raz życie i sprawdza się nie tylko przy zabawach z obiektami typu wrapper.
Druga nauka to taka, że jak okazuje się żadna książka nigdy nie zastąpi nam do końca specyfikacji czy dokumentacji. Polecam Unixową mantrę RTFM, chociaż z drugiej strony, kto ma na to czas ;)

6 komentarzy:

Anonimowy pisze...

Znowu pudło. Zaznaczając odpowiedź wiedziałem, że to na pewno nie to - było by to zbyt banalne. Także następnym razem wybiorę najmniej logiczną odpowiedź, wtedy powinno się udać ;-)

Anonimowy pisze...

Ten trik akurat znałem. Jak byłem mało obyty z programowaniem, to kilka razy się na tym wyłożyłem - podczas testów wszystko działało OK, a nagle w produkcji wychodziły buraki, bo sekwencja, z której był ustawiany klucz główny w bazie przekroczyła 128.

eximius pisze...

juz miesiac temu Ci o tym mowilem :P
a dowiedzialem sie od Piotrka z PM.

pozdrawiam

Anonimowy pisze...

cholera, zaczynam sie czuc jak uczniak... za co ja mam ten SCJP? ;D

Anonimowy pisze...

Szczerze mówiąc jak dla mnie brakuje odpowiedzi, że kod się nie skompiluje. :)

Czy może jest jakaś autopromocja prymitywa do Integera??

Paweł pisze...

czyzby choidzlo ci o autoboxing od javy 1.5 ? ;)