metodo - make object copy java
clone() vs copy constructor vs factory method? (10)
Hice un rápido google en la implementación de clone () en Java y encontré: http://www.javapractices.com/topic/TopicAction.do?Id=71
Tiene el siguiente comentario:
Los constructores de copia y los métodos de fábrica estáticos proporcionan una alternativa al clon y son mucho más fáciles de implementar.
Todo lo que quiero hacer es hacer una copia profunda. La implementación de clone () parece tener mucho sentido, pero este artículo altamente clasificado en google me da un poco de miedo.
Estos son los problemas que he notado:
Los constructores de copia no funcionan con Generics.
Aquí hay un pseudo código que no compilará.
public class MyClass<T>{
..
public void copyData(T data){
T copy=new T(data);//This isn''t going to work.
}
..
}
Muestra 1: Usar un constructor de copia en una clase genérica.
Los métodos de fábrica no tienen nombres estándar.
Es bastante agradable tener una interfaz para código reutilizable.
public class MyClass<T>{
..
public void copyData(T data){
T copy=data.clone();//Throws an exception if the input was not cloneable
}
..
}
Muestra 2: Usar clone () en una clase genérica.
Noté que el clon no es un método estático, pero ¿no sería necesario hacer copias en profundidad de todos los campos protegidos? Al implementar clone (), el esfuerzo adicional para lanzar excepciones en subclases no clonables me parece trivial.
¿Me estoy perdiendo de algo? Cualquier idea sería apreciada.
A continuación hay algunos inconvenientes debido a los cuales muchos desarrolladores no usan Object.clone ()
- El método Object.clone () requiere que agreguemos mucha sintaxis a nuestro código, como implementar la interfaz Cloneable, definir el método clone () y manejar CloneNotSupportedException y finalmente llamar a Object.clone () y convertirlo en nuestro objeto.
- La interfaz clonable carece del método clone (), en realidad, Cloneable es una interfaz de marcador y no tiene ningún método y aún así debemos implementarlo solo para decirle a JVM que podemos realizar clone () en nuestro objeto.
- Object.clone () está protegido, por lo que debemos proporcionar nuestro propio clone () e indirectamente llamar a Object.clone () desde él.
- No tenemos ningún control sobre la construcción de objetos porque Object.clone () no invoca ningún constructor.
- Si estamos escribiendo un método de clonación en una clase secundaria, por ejemplo, Persona, entonces todas sus superclases deben definir el método clone () en ellas o heredarlo de otra clase padre, de lo contrario la cadena super.clone () fallará.
- Object.clone () admite solo una copia poco profunda, por lo que los campos de referencia de nuestro objeto recién clonado aún mantendrán objetos que los campos de nuestro objeto original tenían. Para superar esto, necesitamos implementar clone () en cada clase cuya referencia está reteniendo nuestra clase y luego llamar a su clon por separado en nuestro método clone () como en el ejemplo siguiente.
- No podemos manipular los campos finales en Object.clone () porque los campos finales solo se pueden cambiar a través de constructores. En nuestro caso, si queremos que cada Objeto de la Persona sea único por ID, obtendremos el objeto duplicado si usamos Object.clone () porque Object.clone () no llamará al constructor y el campo de Id. Final no se puede modificar de Person.clone ().
Los constructores de copia son mejores que Object.clone () porque
- No nos obligue a implementar ninguna interfaz ni a lanzar ninguna excepción, pero seguramente podemos hacerlo si es necesario.
- No requiere ningún yeso.
- No nos pida que dependamos de un mecanismo de creación de objetos desconocido.
- No requiere que la clase de padres siga ningún contrato ni implemente nada.
- Permitirnos modificar los campos finales
- Permítanos tener un control completo sobre la creación de objetos, podemos escribir nuestra lógica de inicialización en él.
Lea más sobre Java Cloning - Copiar Constructor versus Clonación
Básicamente, el clon está roto . Nada funcionará con los genéricos fácilmente. Si tiene algo así (abreviado para transmitir el mensaje):
public class SomeClass<T extends Copyable> {
public T copy(T object) {
return (T) object.copy();
}
}
interface Copyable {
Copyable copy();
}
Luego, con una advertencia del compilador, puede hacer el trabajo. Debido a que los genéricos se borran en el tiempo de ejecución, algo que hace una copia va a tener una advertencia de compilación generando un molde. No es evitable en este caso. . Es evitable en algunos casos (gracias, kb304) pero no en todos. Considere el caso en el que tiene que admitir una subclase o una clase desconocida que implementa la interfaz (como cuando estaba iterando a través de una colección de archivos que no generaban necesariamente la misma clase).
Creo que la respuesta de Yishai podría mejorarse para que no tengamos advertencia con el siguiente código:
public class SomeClass<T extends Copyable<T>> {
public T copy(T object) {
return object.copy();
}
}
interface Copyable<T> {
T copy();
}
De esta forma, una clase que necesite implementar la interfaz Copyable debe ser así:
public class MyClass implements Copyable<MyClass> {
@Override
public MyClass copy() {
// copy implementation
...
}
}
Java no tiene constructores de copia en el mismo sentido que C ++.
Puede tener un constructor que tome un objeto del mismo tipo como argumento, pero pocas clases lo admiten. (menos que el número que soporte clon capaz)
Para un clon genérico, tengo un método auxiliar que crea una nueva instancia de una clase y copia los campos del original (una copia poco profunda) usando reflexiones (en realidad algo así como reflexiones pero más rápido)
Para una copia profunda, un enfoque simple es serializar el objeto y deserializarlo.
Por cierto: mi sugerencia es usar objetos inmutables, entonces no necesitarás clonarlos. ;)
La interfaz Cloneable está rota, en el sentido de que es inútil, pero la clonación funciona bien y puede conducir a un mejor rendimiento para objetos grandes: 8 campos y más, pero luego fallará en el análisis de escape. es preferible usar copy constructor la mayor parte del tiempo. Usar clon en array es más rápido que Arrays.copyOf porque se garantiza que la longitud será la misma.
más detalles aquí https://arnaudroger.github.io/blog/2017/07/17/deep-dive-clone-vs-copy.html
Lo que se está perdiendo es que el clon crea copias superficiales por defecto y por convención, y que hacer que cree copias profundas, en general, no es factible.
El problema es que realmente no se pueden crear copias profundas de gráficos de objetos cíclicos, sin poder hacer un seguimiento de qué objetos se han visitado. clone () no proporciona dicho seguimiento (ya que tendría que ser un parámetro para .clone ()) y, por lo tanto, solo crea copias superficiales.
Incluso si su propio objeto invoca .clone para todos sus miembros, todavía no será una copia profunda.
Por lo general, clone () trabaja en conjunto con un constructor de copia protegida. Esto se hace porque clone (), a diferencia de un constructor, puede ser virtual.
En un cuerpo de clase para derivado de una base de clase superior tenemos
class Derived extends Base {
}
Por lo tanto, en su forma más simplista, agregaría a esto un constructor de copia virtual con clone (). (En C ++, Joshi recomienda clonar como el constructor de copia virtual.)
protected Derived() {
super();
}
protected Object clone() throws CloneNotSupportedException {
return new Derived();
}
Se vuelve más complicado si desea llamar a super.clone () como se recomienda y debe agregar estos miembros a la clase, puede intentar esto
final String name;
Address address;
/// This protected copy constructor - only constructs the object from super-class and
/// sets the final in the object for the derived class.
protected Derived(Base base, String name) {
super(base);
this.name = name;
}
protected Object clone() throws CloneNotSupportedException {
Derived that = new Derived(super.clone(), this.name);
that.address = (Address) this.address.clone();
}
Ahora, si una ejecución, tienes
Base base = (Base) new Derived("name");
y luego lo hiciste
Base clone = (Base) base.clone();
esto invocaría, clone () en la clase Derivada (la de arriba), esto invocaría a super.clone () - que puede o no implementarse, pero se le aconseja que lo llame. La implementación pasa la salida de super.clone () a un constructor de copia protegido que toma una base y le pasa cualquier miembro final.
Ese constructor copia invoca el constructor de copia de la superclase (si sabe que tiene una) y establece las finales.
Cuando vuelve al método clone (), establece los miembros no finales.
Los lectores astutos notarán que si tiene un constructor de copias en la Base, será llamado por super.clone () - y se le volverá a llamar cuando invoque al superconstructor en el constructor protegido, por lo que puede estar llamando al super copia-constructor dos veces. Con suerte, si está bloqueando recursos, lo sabrá.
Si uno no está al 100% al tanto de todas las peculiaridades de clone()
, le aconsejaría que se mantenga alejado de él. Yo no diría que clone()
roto. Yo diría: úsala solo cuando estés completamente seguro de que es tu mejor opción. Un constructor de copia (o método de fábrica, que realmente no importa, creo) es fácil de escribir (quizás sea largo, pero fácil), solo copia lo que desea copiar y copia de la forma en que desea que se copien las cosas. Puede recortarlo según sus necesidades exactas.
Además: es fácil depurar lo que sucede cuando llamas a tu constructor de copia / método de fábrica.
Y clone()
no crea una copia "profunda" de su objeto al instante, asumiendo que quiere decir que no solo se copian las referencias (por ej., A una Collection
). Pero lea más sobre lo profundo y lo superficial aquí: Copia profunda, copia superficial, clon
También está el patrón de Constructor. Ver Java efectivo para más detalles.
No entiendo tu evaluación. En un constructor de copia usted es plenamente consciente del tipo, ¿por qué hay una necesidad de usar genéricos?
public class C {
public int value;
public C() { }
public C(C other) {
value = other.value;
}
}
Hubo una pregunta similar recientemente here .
public class G<T> {
public T value;
public G() { }
public G(G<? extends T> other) {
value = other.value;
}
}
Una muestra ejecutable:
public class GenTest {
public interface Copyable<T> {
T copy();
}
public static <T extends Copyable<T>> T copy(T object) {
return object.copy();
}
public static class G<T> implements Copyable<G<T>> {
public T value;
public G() {
}
public G(G<? extends T> other) {
value = other.value;
}
@Override
public G<T> copy() {
return new G<T>(this);
}
}
public static void main(String[] args) {
G<Integer> g = new G<Integer>();
g.value = 1;
G<Integer> f = g.copy();
g.value = 2;
G<Integer> h = copy(g);
g.value = 3;
System.out.printf("f: %s%n", f.value);
System.out.printf("g: %s%n", g.value);
System.out.printf("h: %s%n", h.value);
}
}
Un patrón que puede funcionar para usted es la copia de nivel de frijol. Básicamente utilizas un constructor sin argumentos y llamas a varios setters para proporcionar los datos. Incluso puede usar las diversas bibliotecas de propiedades de frijoles para establecer las propiedades con relativa facilidad. Esto no es lo mismo que hacer un clon (), pero para muchos fines prácticos está bien.