É um pouco difícil implementar uma função de cópia de objeto profundo. Quais etapas você toma para garantir que o objeto original e o clonado não compartilhem nenhuma referência?
É um pouco difícil implementar uma função de cópia de objeto profundo. Quais etapas você toma para garantir que o objeto original e o clonado não compartilhem nenhuma referência?
Respostas:
Uma maneira segura é serializar o objeto e desserializar. Isso garante que tudo seja uma nova referência.
Aqui está um artigo sobre como fazer isso com eficiência.
Advertências: É possível que as classes substituam a serialização, de modo que novas instâncias não sejam criadas, por exemplo, para singletons. Além disso, é claro que isso não funciona se suas aulas não forem serializáveis.
Algumas pessoas mencionaram o uso ou substituição Object.clone()
. Não faça isso. Object.clone()
apresenta alguns problemas importantes e seu uso é desencorajado na maioria dos casos. Consulte o Item 11, do " Java Efetivo ", de Joshua Bloch, para obter uma resposta completa. Eu acredito que você pode usar com segurança Object.clone()
em matrizes de tipo primitivo, mas, além disso, você precisa ser criterioso ao usar e substituir o clone corretamente.
Os esquemas que dependem da serialização (XML ou não) são kludgy.
Não há uma resposta fácil aqui. Se você deseja copiar um objeto em profundidade, precisará percorrer o gráfico do objeto e copiar cada objeto filho explicitamente através do construtor de cópia do objeto ou de um método estático de fábrica que, por sua vez, copia profundamente o objeto filho. Imutáveis (por exemplo, String
s) não precisam ser copiados. Como um aparte, você deve favorecer a imutabilidade por esse motivo.
Você pode fazer uma cópia profunda com serialização sem criar arquivos.
Seu objeto que você deseja copiar em profundidade precisará implement serializable
. Se a classe não for final ou não puder ser modificada, estenda a classe e implemente serializável.
Converta sua classe em um fluxo de bytes:
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(object);
oos.flush();
oos.close();
bos.close();
byte[] byteData = bos.toByteArray();
Restaure sua classe a partir de um fluxo de bytes:
ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
(Object) object = (Object) new ObjectInputStream(bais).readObject();
instance
nesse caso?
Você pode criar um clone profundo baseado em serialização usando o org.apache.commons.lang3.SerializationUtils.clone(T)
Apache Commons Lang, mas tenha cuidado - o desempenho é péssimo.
Em geral, é uma prática recomendada escrever seus próprios métodos de clone para cada classe de um objeto no gráfico de objetos que precisam de clonagem.
org.apache.commons.lang.SerializationUtils
Uma maneira de implementar a cópia profunda é adicionar construtores de cópia a cada classe associada. Um construtor de cópia pega uma instância de 'this' como seu argumento único e copia todos os valores dele. Bastante trabalho, mas bem direto e seguro.
EDIT: observe que você não precisa usar métodos de acesso para ler os campos. Você pode acessar todos os campos diretamente porque a instância de origem é sempre do mesmo tipo que a instância com o construtor de cópia. Óbvio, mas pode ser esquecido.
Exemplo:
public class Order {
private long number;
public Order() {
}
/**
* Copy constructor
*/
public Order(Order source) {
number = source.number;
}
}
public class Customer {
private String name;
private List<Order> orders = new ArrayList<Order>();
public Customer() {
}
/**
* Copy constructor
*/
public Customer(Customer source) {
name = source.name;
for (Order sourceOrder : source.orders) {
orders.add(new Order(sourceOrder));
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Editar: Observe que, ao usar construtores de cópia, você precisa conhecer o tipo de tempo de execução do objeto que está copiando. Com a abordagem acima, você não pode copiar facilmente uma lista mista (poderá fazê-lo com algum código de reflexão).
Toyota
, seu código colocará um Car
na lista de destinos. A clonagem adequada geralmente exige que a classe forneça um método de fábrica virtual cujo contrato declare que ele retornará um novo objeto de sua própria classe; o próprio contratante de cópia deve protected
garantir que ele seja usado apenas para construir objetos cujo tipo preciso corresponda ao do objeto que está sendo copiado).
Você pode usar uma biblioteca que possui uma API simples e executa uma clonagem relativamente rápida com reflexão (deve ser mais rápida que os métodos de serialização).
Cloner cloner = new Cloner();
MyClass clone = cloner.deepClone(o);
// clone is a deep-clone of o
O Apache commons oferece uma maneira rápida de clonar profundamente um objeto.
My_Object object2= org.apache.commons.lang.SerializationUtils.clone(object1);
O XStream é realmente útil nesses casos. Aqui está um código simples para fazer clonagem
private static final XStream XSTREAM = new XStream();
...
Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj));
Uma abordagem muito fácil e simples é usar o Jackson JSON para serializar objetos Java complexos em JSON e lê-los novamente.
Para usuários do Spring Framework . Usando classe org.springframework.util.SerializationUtils
:
@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T object) {
return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object));
}
Para objetos complicados e quando o desempenho não é significativo, eu uso uma biblioteca json, como gson para serializar o objeto em texto json, depois desserializa o texto para obter um novo objeto.
O gson, que com base na reflexão, funciona na maioria dos casos, exceto que os transient
campos não serão copiados e os objetos com referência circular com causa StackOverflowError
.
public static <T> T copy(T anObject, Class<T> classInfo) {
Gson gson = new GsonBuilder().create();
String text = gson.toJson(anObject);
T newObject = gson.fromJson(text, classInfo);
return newObject;
}
public static void main(String[] args) {
String originalObject = "hello";
String copiedObject = copy(originalObject, String.class);
}
Use o XStream ( http://x-stream.github.io/ ). Você pode controlar quais propriedades você pode ignorar por meio de anotações ou especificar explicitamente o nome da propriedade para a classe XStream. Além disso, você não precisa implementar interface clonável.
A cópia em profundidade só pode ser feita com o consentimento de cada classe. Se você tiver controle sobre a hierarquia de classes, poderá implementar a interface clonável e o método Clone. Caso contrário, é impossível fazer uma cópia detalhada com segurança, porque o objeto também pode estar compartilhando recursos que não são de dados (por exemplo, conexões com o banco de dados). Em geral, porém, a cópia profunda é considerada uma prática ruim no ambiente Java e deve ser evitada por meio das práticas de design apropriadas.
import com.thoughtworks.xstream.XStream;
public class deepCopy {
private static XStream xstream = new XStream();
//serialize with Xstream them deserialize ...
public static Object deepCopy(Object obj){
return xstream.fromXML(xstream.toXML(obj));
}
}
O BeanUtils faz um ótimo trabalho de clonagem profunda de beans.
BeanUtils.cloneBean(obj);
1)
public static Object deepClone(Object object) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(object);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}
2)
// (1) create a MyPerson object named Al
MyAddress address = new MyAddress("Vishrantwadi ", "Pune", "India");
MyPerson al = new MyPerson("Al", "Arun", address);
// (2) make a deep clone of Al
MyPerson neighbor = (MyPerson)deepClone(al);
Aqui, sua classe MyPerson e MyAddress deve implementar uma interface serilazable
Usando Jackson para serializar e desserializar o objeto. Esta implementação não requer que o objeto implemente a classe Serializable.
<T> T clone(T object, Class<T> clazzType) throws IOException {
final ObjectMapper objMapper = new ObjectMapper();
String jsonStr= objMapper.writeValueAsString(object);
return objMapper.readValue(jsonStr, clazzType);
}