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 gete ()) 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, ImmutableListpor exemplo) e preparar os objetos fooou barpara a getchamada.
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 getor is, mas essa é uma prática bastante terrível ...
String, intou 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;.
Objectainda é 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(...).
finalnão torna uma variável o objeto imutável. Eu normalmente uso um design em que definofinalcampos 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 quaisquersetXmétodos.