Vi que isso tem a ver com imutabilidade de objetos, algum pode explicar?
As traduções para o português são esquisitas. Aonde você viu esse termo “cópia defensiva”?
Cópia defensiva é basicamente a cópia de um objeto onde você altera seis atributos sem alterar os atributos do objeto original.
É bastante utilizado com objetos imutáveis justamente pois você não consegue alterar os atributos de um objeto imutável.
Então você cria uma cópia do objeto e nessa cópia inicializa os atributos com outros valores que não são possíveis de alterar no objeto original.
A ideia da cópia defensiva é evitar que um objeto seja alterado em lugares que não deveria.
Por exemplo, se eu tiver uma classe assim:
public class Pessoa {
private String nome;
private Date dataNascimento;
public Pessoa(String nome, Date dataNascimento) {
this.nome = nome;
this.dataNascimento = dataNascimento;
}
public String getNome() {
return this.nome;
}
public Date getDataNascimento() {
return this.dataNascimento;
}
}
Eu posso fazer o seguinte:
Date dataNascimento = new Date(); // pessoa nasceu hoje
Pessoa pessoa = new Pessoa("Fulano", dataNascimento);
System.out.println("Antes: " + pessoa.getDataNascimento());
// mudei a data de nascimento
dataNascimento.setTime(0);
System.out.println("Depois: " + pessoa.getDataNascimento());
Eu criei a data de nascimento como sendo a data atual. Mas como java.util.Date
é uma classe mutável (ou seja, é possível alterar os valores dos seus campos), eu posso alterá-la, e essas alterações acabam refletindo no objeto pessoa
, já que ele possui a mesma referência dentro dele (o atributo dataNascimento
aponta para a mesma instância de Date
que foi criada fora do objeto pessoa
).
Por isso que ao mudar a data fora do objeto pessoa
, esta mudança também se refletiu dentro do objeto. A saída será algo assim (obviamente as datas podem variar de acordo com o instante em que você roda o código, além do timezone default que estiver configurado na JVM, mas o ponto importante é que a dataNascimento
dentro do objeto pessoa
muda):
Antes: Mon Apr 04 08:42:38 BRT 2022
Depois: Wed Dec 31 21:00:00 BRT 1969
Para evitar isso, você pode fazer uma cópia defensiva no construtor de Pessoa
:
public Pessoa(String nome, Date dataNascimento) {
this.nome = nome;
// criando cópia defensiva
this.dataNascimento = new Date(dataNascimento.getTime());
}
Ou seja, eu crio uma nova data (um novo objeto, uma nova instância de Date
), contendo o mesmo valor do argumento passado para o construtor. Agora qualquer alteração feita no Date
original não se reflete dentro do objeto pessoa
, já que agora ele tem outra instância de Date
. Rodando novamente o código acima, agora ele imprimirá a mesma data duas vezes.
Para o nome
eu não precisei fazer isso porque String
's são imutáveis em Java, então não tem como alterar a string original (não tem nenhum setAlgumaCoisa
que mude o valor da String
, por isso não há a necessidade de fazer uma cópia defensiva). Então se você usasse uma classe imutável para a data (por exemplo, java.time.LocalDate
), não seria necessário fazer a cópia.
Mas não para por aí. Mesmo com o construtor fazendo a cópia defensiva, eu ainda poderia fazer isso:
Pessoa pessoa = new Pessoa("Fulano", new Date());
System.out.println("Antes: " + pessoa.getDataNascimento());
// mudei a data de nascimento
pessoa.getDataNascimento().setTime(0);
System.out.println("Depois: " + pessoa.getDataNascimento());
Ou seja, ainda dá para mudar a data porque o getter retorna a própria dataNascimento
, então qualquer alteração feita nela se refletirá no objeto pessoa
.
Para evitar isso, o getter também poderia retornar outra cópia defensiva:
public Date getDataNascimento() {
return new Date(this.dataNascimento.getTime());
}
E agora o código acima imprime a mesma data duas vezes, já que o getter retorna uma cópia, e portanto qualquer alteração feita na data retornada por ele não altera a data original da pessoa
.
É claro que nem tudo precisa disso, você precisa avaliar para quais campos vale a pena ou é realmente necessário criar cópias defensivas, e isso varia muito caso a caso.