Eu vejo os POJOs mais imutáveis escritos assim:
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;
}
}
No entanto, tenho a tendência de escrevê-los assim:
public class MyObject {
public final String foo;
public final int bar;
public MyObject(String foo, int bar) {
this.foo = foo;
this.bar = bar;
}
}
Observe que as referências são finais, portanto o objeto ainda é imutável. Deixa-me escrever menos código e permite mais curto (por 5 caracteres: o get
e ()
) acesso.
A única desvantagem que vejo é que, se você deseja alterar a implementação do getFoo()
caminho para fazer algo louco, não pode. Mas realisticamente, isso nunca acontece porque o Objeto é imutável; você pode verificar durante a instanciação, criar cópias defensivas imutáveis durante a instanciação (consulte Goiabas, ImmutableList
por exemplo) e preparar os objetos foo
ou bar
para a get
chamada.
Faltam algumas desvantagens?
EDITAR
Suponho que outra desvantagem que estou perdendo são as bibliotecas de serialização usando reflexão sobre os métodos que começam com get
or is
, mas essa é uma prática bastante terrível ...
String
, int
ou MyObject
. Na primeira versão, final
é apenas para garantir que outros métodos além do construtor da classe não tentem bar = 7;
, por exemplo. Na segunda versão, final
é necessário evitar que os consumidores a fazer: MyObject x = new MyObject("hi", 5); x.bar = 7;
.
Object
ainda é imutável " é enganoso - dessa maneira, parece que você acha que algum final Object
é imutável, o que não é. Desculpe pelo mal entendido.
myObj.getFoo().setFrob(...)
.
final
não torna uma variável o objeto imutável. Eu normalmente uso um design em que definofinal
campos antes de criar retorno de chamada para que o retorno de chamada possa acessar esses campos. Obviamente, ele pode chamar todos os métodos, incluindo quaisquersetX
métodos.