W3docs

Clonagem de Objetos Java

Copie objetos Java com clone(), a interface Cloneable e as diferenças entre cópias rasas e profundas.

Clonar um objeto significa produzir um novo objeto com o mesmo estado — uma cópia que pode ser mutada independentemente do original. A resposta nativa do Java é Object.clone() combinado com a interface marcadora Cloneable, mas o design tem arestas suficientes para que a maior parte do código moderno recorra a um construtor de cópia ou a um método fábrica no lugar. Este capítulo mostra as duas rotas e a armadilha que existe entre elas.

A rota nativa: Object.clone()

Object.clone() é protected e realiza uma cópia rasa campo a campo da instância. Para usá-lo você precisa:

  1. Fazer sua classe implementar Cloneable — uma interface marcadora sem métodos. Sem ela, clone() lança CloneNotSupportedException.
  2. Sobrescrever clone() para torná-lo public e (geralmente) restringir o tipo de retorno à classe concreta.
public class Box implements Cloneable {
  int size;

  @Override
  public Box clone() {
    try {
      return (Box) super.clone();
    } catch (CloneNotSupportedException e) {
      throw new AssertionError(e);   // can't happen — we implement Cloneable
    }
  }
}

super.clone() produz a cópia real. O try/catch é burocracia: a exceção verificada está declarada em Object.clone, mas como nossa classe implementa Cloneable, a exceção é inatingível.

Cópia rasa: o que ela realmente faz

Uma cópia rasa duplica os campos imediatos. Referências dentro do objeto são copiadas como referências, não como novos objetos — portanto o original e o clone compartilham o que essas referências apontam:

public class Person implements Cloneable {
  String name;
  int[]  scores;

  @Override
  public Person clone() {
    try { return (Person) super.clone(); }
    catch (CloneNotSupportedException e) { throw new AssertionError(e); }
  }
}

Person a = new Person();
a.scores = new int[]{1, 2, 3};
Person b = a.clone();
b.scores[0] = 99;
System.out.println(a.scores[0]);   // 99 — they share the same array

Para primitivos e valores imutáveis (String, Integer, LocalDate), a cópia rasa está bem. Para sub-objetos mutáveis, quase sempre está errada — mutar o clone afeta o original.

Cópia profunda: a correção

Para obter uma cópia verdadeiramente independente, sobrescreva clone() para copiar recursivamente os campos mutáveis:

@Override
public Person clone() {
  try {
    Person copy   = (Person) super.clone();
    copy.scores   = scores.clone();          // arrays have their own clone()
    return copy;
  } catch (CloneNotSupportedException e) {
    throw new AssertionError(e);
  }
}

Arrays implementam Cloneable nativamente e copiam com .clone(). Coleções não — para um campo List<String> você escreveria copy.items = new ArrayList<>(items); (veja ArrayList). Para um grafo de seus próprios tipos mutáveis, todos os tipos do grafo precisam participar.

Por que Cloneable tem má reputação

Algumas peculiaridades tornam o clone() desconfortável:

  • É uma interface marcadora sem nenhum método clone() — o próprio Cloneable não expõe nada; o contrato está em Object.clone().
  • Bypassa construtores — os campos do novo objeto são preenchidos pela JVM, portanto quaisquer invariantes impostos no construtor não são revalidados.
  • Subclasses herdam a obrigação: se Parent sobrescreve clone(), toda subclasse precisa manter a lógica de cópia profunda em sincronia, ou herda silenciosamente uma versão rasa quebrada.
  • A exceção verificada, o cast, a chamada a super.clone() — cada sobrescrita repete o mesmo ruído.

A alternativa moderna: construtor de cópia

Um construtor de cópia é simplesmente um construtor que recebe uma instância da mesma classe e copia seus campos:

public class Person {
  String name;
  int[]  scores;

  public Person(Person other) {
    this.name   = other.name;
    this.scores = other.scores.clone();   // deep where it matters
  }
}

Person b = new Person(a);

Ele passa pelo construtor normal, então os invariantes são verificados. É Java puro — sem interface marcadora, sem CloneNotSupportedException, sem cast. Subclasses apenas escrevem seu próprio construtor de cópia que chama super(other). A recomendação do Effective Java é preferir construtores de cópia (ou fábricas estáticas copyOf) ao clone.

Classes parecidas com coleções já seguem esse padrão: new ArrayList<>(other), new HashMap<>(other), Set.copyOf(other).

Qual abordagem devo usar?

SituaçãoAbordagem recomendada
Classe nova e simples que você controlaConstrutor de cópia ou fábrica estática copyOf
Classe com apenas campos primitivos ou imutáveisQualquer uma — até um clone() raso é seguro
Classe com campos mutáveis (listas, arrays, objetos aninhados)Construtor de cópia com cópias profundas explícitas
API existente que já exige CloneableSobrescreva clone() e faça cópia profunda dos campos mutáveis
Tipo valor que você pode redesenharTorne-o imutável — assim nenhuma cópia é necessária

Em resumo: prefira um construtor de cópia para código novo e recorra a clone() apenas quando um contrato existente obrigar.

Records e tipos imutáveis

Records são imutáveis, portanto não precisam de clonagem alguma — compartilhe a mesma referência em todo lugar. Se precisar de uma cópia modificada, escreva pequenos métodos with...:

record Point(int x, int y) {
  Point withX(int newX) { return new Point(newX, y); }
}

Esse estilo — "construir uma nova instância com um campo alterado" — costuma ser mais claro do que clonar seguido de mutação.

Um exemplo completo

java— editable, runs on the server

O que vem a seguir

A maior parte dos problemas com clonagem desaparece se a classe for imutável desde o início — nada para copiar defensivamente, sem surpresas de aliasing, seguro para compartilhar entre threads. O próximo capítulo explica o que é preciso para projetar uma classe dessa forma. Continue em classes imutáveis em Java.

Prática

Prática
O que o `Object.clone()` padrão faz com um campo que contém uma lista mutável?
O que o `Object.clone()` padrão faz com um campo que contém uma lista mutável?
Was this page helpful?