O que são cópias defensivas de objetos e para que servem?

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”?

1 curtida

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.

2 curtidas

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.

4 curtidas