Pular para conteúdo

Cópia Defensiva e Imutabilidade

A cópia defensiva estabelece posse ao impedir que chamador e receptor alterem o mesmo estado inesperadamente. A imutabilidade vai além: após a construção, o estado observável nunca muda. Ambas reduzem acoplamento temporal e facilitam compartilhamento seguro.

Cópia na entrada e na saída

public final class Route {
    private final List<String> stops;

    public Route(Collection<String> stops) {
        this.stops = List.copyOf(stops);
        if (this.stops.stream().anyMatch(Objects::isNull)) {
            throw new IllegalArgumentException("stops cannot contain null");
        }
    }

    public List<String> stops() {
        return stops;
    }
}

List.copyOf impede modificação estrutural pela lista retornada e a separa da fonte mutável. É cópia rasa: elementos mutáveis ainda permitem alterar estado alcançável. Imutabilidade profunda exige elementos imutáveis ou cópias em todo limite de posse.

Arrays, Date, builders, buffers e coleções frequentemente precisam de cópias. Uma view não modificável não é cópia; mudanças por outra referência continuam visíveis.

Records e builders

Records reduzem sintaxe, mas não tornam componentes profundamente imutáveis. Use construtor compacto para normalizar e copiar. Um builder pode ser mutável durante a construção, mas o objeto final não deve reter seu armazenamento mutável.

Concorrência e publicação

Objetos imutáveis bem construídos são mais fáceis de publicar e compartilhar. Isso não torna atômicas operações sobre recursos mutáveis externos. Um snapshot também fica obsoleto; atualidade é outro contrato.

Compromissos

Cópias custam tempo e memória. Prefira transferência clara de posse, valores imutáveis, estruturas persistentes ou snapshots limitados. Meça antes de evitar uma cópia necessária e documente APIs que emprestam armazenamento do chamador.

Teste mutação das entradas, retornos e elementos aninhados, além da estabilidade de igualdade e hash em coleções ou chaves de cache.