Widzę najbardziej niezmienne POJO napisane w ten sposób:
public class MyObject {
private final String foo;
private final int bar;
public MyObject(String foo, int bar) {
this.foo = foo;
this.bar = bar;
}
public String getFoo() {
return foo;
}
public int getBar() {
return bar;
}
}
Mimo to mam tendencję do pisania ich w ten sposób:
public class MyObject {
public final String foo;
public final int bar;
public MyObject(String foo, int bar) {
this.foo = foo;
this.bar = bar;
}
}
Zauważ, że odniesienia są ostateczne, więc Obiekt jest nadal niezmienny. Pozwala mi pisać mniej kodu i umożliwia krótszy (o 5 znaków: dostęp get
i ()
).
Jedyną wadą, jaką widzę, jest to, że jeśli chcesz zmienić implementację w getFoo()
dół drogi, aby zrobić coś szalonego, nie możesz. Ale realistycznie to się nie zdarza, ponieważ Obiekt jest niezmienny; możesz zweryfikować podczas tworzenia instancji, tworzyć niezmienne kopie obronne podczas tworzenia instancji (patrz ImmutableList
na przykład Guava ) i przygotować obiekty foo
lub bar
obiekty na get
wezwanie.
Czy brakuje mi wad?
EDYTOWAĆ
Przypuszczam, że kolejną wadą, której mi brakuje, są biblioteki serializacji wykorzystujące refleksję nad metodami zaczynającymi się od get
lub is
, ale to dość okropna praktyka ...
String
, int
lub MyObject
. W pierwszej wersji final
ma jedynie zapewnić, że metody inne niż konstruktor w klasie nie będą próbować bar = 7;
na przykład. W drugiej wersji, final
jest konieczne, aby zapobiec konsumentów robi: MyObject x = new MyObject("hi", 5); x.bar = 7;
.
Object
nadal są niezmienne. ” Są mylące - w ten sposób wydaje się, że uważasz, że każdy final Object
jest niezmienny, a tak nie jest. Przepraszam za nieporozumienie.
myObj.getFoo().setFrob(...)
.
final
nie czyni zmiennej niezmienną. Zwykle używam projektu, w którym definiujęfinal
pola przed utworzeniem wywołania zwrotnego, aby wywołanie zwrotne mogło uzyskać dostęp do tych pól. Może oczywiście wywoływać wszystkie metody, w tym dowolnesetX
.