Quando é referencia para um objeto ou para uma array, precisamos mesmo fazer uma copia defensiva. É a sugestao do Effective Java. Realmente tem esse problema que voce falou, em as vezes isso causar um deep cloning “deep demais”. Mas normalmente se estamos trabalhando com uma API mais voltada a imutabilidade (como o Joda Time), os objetos pelos quais nos somos compostos ja sao imutaveis, entao ai nao precisa fazer deep cloning.
O seu exemplo me pareceu totalmente imutável, ou seja, suas propriedades são efetivamente read-only.
String e Date são imutáveis, logo os seus getters estão seguros.
Ruby tem um método interessante que é o freeze que te permite transformar um objecto mutável em imutável. Java não tem isso, logo o que se tem são objetos diferentes, um mutável e outro imutável. Ex: String e StringBuilder; Date e Calendar, etc.
Java tem até uma história interessante que é o fato de nas primeiras versões por erro mesmo o java.util.Date era mutável !!! Corrigiram isso rapidamente nas versões posteriores mas o cara que fez isso deve ter tomada uma bela chamada.
Sergio, se voce executar o exemplo vera que a saida eh 2005 e depois 2008.
Mas usei Date so como exemplo, para tentar, de maneira simples, ilustrar o problema.
Pelo jeito nao existe algo simples para garantir o encapsulamento para objetos e arrays (como o Paulo comentou).
Seria legal se existisse uma qualificador readOnly ou algo assim para o valor de retorno/ou variavel de classe (nao apenas para a referencia do objeto), mas tenho certeza que isso nao seria facil (ou talvez nem viavel) de ser implementado pela JVM, senao ja estaria disponivel.
package testgetters;
import java.util.Date;
public Date getDOB() {
return new Date(objDOB.getTime());
}
public void setDOB(Date dob){
this.objDOB = new Date(dob.getTime());
}
}
Entao, isso funciona sim para objetos nao aninhados (Corrija-me se estiver enganado).
Mas o problema permaneceria em casos de objetos que tivessem outros objetos aninhados. Para ser realmente efetivo, teriamos que “reinstanciar” um novo objeto (nos getters) para cada propriedade publica dos objetos aninhados (menos para tipos primitivos).
Eu acho que isso impactaria negativamente na performance, nao? E, na minha opiniao, tambem nao seria uma solucao elegante (as vezes temos que contar com solucoes nao elegantes…rs).
O Sergio apontou o [color=red]freeze do Ruby[/color] e apesar de apenas ter dado uma rapida olhada, pareceu ser o que eu gostaria de encontrar no Java, para, de forma simples e descritiva, protejer o encapsulamento.
Outro problema que poderia acontecer no meu exemplo inicial eh que um usuario desse objeto poderia alterar o valor de DOB atraves do Getter e todas as constraints do Setter (caso existissem) seriam ignoradas (bye bye encapsulamento).
Muito obrigado por compartilhar sua opiniao.
Abracos
Eh o seguinte, usei DATE como [color=red]um exemplo apenas[/color], poderia ser qualquer outro objeto (ate mesmo um criado pela propria equipe), por exemplo Address (que teria GetStreetName()) poderia estar estar agregado a classe pessoa, ao inves do DATE. Teriamos o mesmo problema.
Sergio, sua dica do freeze foi legal, achei muito pratica e elegante. Espero que tenhamos isso no Java um dia (Nao sei o que isso vai ocasionar de mudancas na JVM).