Finalne referencje
Spis treści
W dzisiejszej pułapce poruszymy zagadnienie lokowania zmiennych w pamięci, ich porównywania i tego co tak naprawdę zmieniamy przekazując argumenty metodom. Dodatkowym zagadnieniem są referencje finalne.
Warto się nad tym zastanowić, ponieważ zarówno początkujący jak i nieco bardziej doświadczeni programiści często mogą wpaść w problemy. Błędy spowodowane z zagadnieniem, które poruszamy może być powodem błędów bardzo trudnych do wykrycia w przypadku skomplikowanej hierarchii klas i tysiącach linii kodu odnalezienie źródła komplikacji zajmie nam wiele godzin.
Poniższy kod przedstawia dwie klasy. Pierwsza z nich posłuży nam do utworzenia obiektu przechowującego jedną wartość typu int. W drugiej przetestujemy różne działania z nią związane.
public class Zmienna {
public Zmienna(int x) {
this.x = x;
}
public int x;
}
public class Test {
static void metoda1(Zmienna z) {
z.x = -1;
z = null;
}
static void metoda2(final Zmienna z) {
z.x = -2;
}
public static void main(String[] args) {
Zmienna a = new Zmienna(1);
final Zmienna b = new Zmienna(2);
//instrukcja 1
metoda1(a);
System.out.print(a.x + " ");
//instrukcja 2
metoda1(b);
System.out.print(b.x + " ");
Zmienna c = new Zmienna(1);
final Zmienna d = new Zmienna(2);
//instrukcja 3
metoda2(c);
System.out.print(c.x + " ");
//instrukcja 4
metoda2(d);
System.out.print(d.x + " ");
}
}
Pytanie jak zwykle: co się stanie po wykonaniu instrukcji 1,2,3,4?
a) NullPointerException ...
b) 1 2 1 2
c) -1 -1 -1 -1
d) -1 -1 -2 -2
e) Inna odpowiedź
Odpowiedź
public class Zmienna {
public Zmienna(int x) {
this.x = x;
}
public int x;
}
public class Test {
static void metoda1(Zmienna z) {
z.x = -1;
z = null;
}
static void metoda2(final Zmienna z) {
z.x = -2;
}
public static void main(String[] args) {
Zmienna a = new Zmienna(1);
final Zmienna b = new Zmienna(2);
//instrukcja 1
metoda1(a);
System.out.print(a.x + " ");
//instrukcja 2
metoda1(b);
System.out.print(b.x + " ");
Zmienna c = new Zmienna(1);
final Zmienna d = new Zmienna(2);
//instrukcja 3
metoda2(c);
System.out.print(c.x + " ");
//instrukcja 4
metoda2(d);
System.out.print(d.x + " ");
}
}
W zadaniu tym mogą wystąpić problemy w kilku miejscach. Po pierwsze, co tak naprawdę zmodyfikuje metoda 1? Przyjmuje ona jako parametr obiekt typu Zmienna. Jej pole x jest publiczne więc jego modyfikacja jest jak najbardziej poprawna. Co jednak w przypadku, gdy przekazujemy do tej metody zmienną (stałą) finalną? Przecież to co pierwsze przychodzi do głowy - nie można tego obiektu zmodyfikować!
Nic bardziej mylnego. W Javie istnieją finalne referencje, a nie finalne obiekty! Nie można więc do referencji a przypisać nowego, lub innego już istniejącego obiektu. Bez problemu można jednak zmieniać widoczne pola, które nie są oznaczone jako final.
Druga sprawa to przypisanie zmiennej null do parametru metody. Oczywiście referencje zmiennych a , ani b nie zostaną zmienione. Dzieje się tak dlatego, że w Javie parametry są przekazywane przez wartość (pass by value), a nie przez referencję. Tak więc referencja z wskazuje na ten sam obiekt, ale nie jest tą samą referencją co zmienna a i b.
Metoda 2 nie przynosi już więcej pułapek.
Wynikiem jest więc odpowiedź d) -1 -1 -2 -2
Dyskusja i komentarze
Masz pytania do tego wpisu? Może chcesz się podzielić spostrzeżeniami? Zapraszamy dyskusji na naszej grupie na Facebooku.