Java: solución recomendada para la clonación profunda/copia de una instancia


Me pregunto si hay una forma recomendada de hacer clon/copia profunda de instancia en java.

Tengo 3 soluciones en mente, pero puedo haber perdido algunas, y me gustaría tener su opinión

Editar: incluir Bohzo propositon y refinar pregunta: se trata más de clonación profunda que de clonación superficial.

Hágalo usted mismo:

Codifique las propiedades clonar a mano después de las propiedades y verifique que las instancias mutables también se clonen.
pro:
- control de lo que se llevará a cabo
- ejecución rápida
contras:
- tedioso de escribir y mantener
- propenso a errores (copiar/pegar fallo, falta de propiedad, reasignado mutable propiedad)

Use reflexión:

Con sus propias herramientas de reflexión o con un ayudante externo (como jakarta common-beans) es fácil escribir un método de copia genérico que hará el trabajo en una línea.
pro:
- fácil de escribir
- sin mantenimiento
contras:
- menos control de lo que sucede
- bug propenso con objeto mutable si la herramienta de reflexión no clona sub objetos demasiado
- ejecución más lenta

Use clone framework:

Use un framework que lo haga por usted, como :
commons-lang SerializationUtils
Biblioteca de Clonación Profunda de Java
Topadora
Kryo

Pro:
- igual que la reflexión
- más control sobre lo que será exactamente ser clonado.
contras:
- cada la instancia mutable está completamente clonada, incluso al final de la jerarquía
- podría ser muy lento para ejecutar

Utilice la instrumentación de bytecode para escribir clon en tiempo de ejecución

Javassit, BCEL o cglib puede ser uso para generar un dedicado cloner tan rápido como una mano escrito. ¿Alguien conoce a un lib usando una de estas herramientas para este propósito ?

¿Qué me he perdido aquí ?
¿Cuál recomendaría ?

Gracias.

 167
Author: Guillaume, 2010-01-28

8 answers

Para la clonación profunda (clona toda la jerarquía de objetos):

  • Commons - lang SerializationUtils - usando serialización - si todas las clases están bajo su control y puede forzar la implementación Serializable.

  • Java Deep Cloning Library - usando reflexión - en los casos en que las clases o los objetos que desea clonar están fuera de su control (una biblioteca de terceros) y no puede hacer que implementen Serializable, o en los casos en que no desea implementar Serializable.

Para clonación superficial (clona solo las propiedades del primer nivel):

Deliberadamente omití la opción "hágalo usted mismo": las API anteriores proporcionan un buen control sobre qué y qué no clonar (por ejemplo, usando transient o String[] ignoreProperties), por lo que no se prefiere reinventar la rueda.

 147
Author: Bozho,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-28 18:30:00

El libro de Joshua Bloch tiene un capítulo entero titulado "Item 10: Override Clone Juiciously" en el cual él entra en porqué overriding clone para la mayor parte es una mala idea porque la especificación de Java para ella crea muchos problemas.

Él proporciona algunas alternativas:

  • Utilice un patrón de fábrica en lugar de un constructor:

         public static Yum newInstance(Yum yum);
    
  • Utilice un constructor de copia:

         public Yum(Yum yum);
    

Todas las clases de colección en Java soportan la copia constructor(por ejemplo, new ArrayList (l);)

 35
Author: LWoodyiii,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-28 22:29:28

Desde la versión 2.07 Kryo soporta clonación superficial / profunda :

Kryo kryo = new Kryo();
SomeClass someObject = ...
SomeClass copy1 = kryo.copy(someObject);
SomeClass copy2 = kryo.copyShallow(someObject);

Kryo es rápido, en el y de su página se puede encontrar una lista de las empresas que lo utilizan en la producción.

 7
Author: Andrey Chaschev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-22 10:29:36

Utilice XStream ToXML/FromXML en memoria. Extremadamente rápido y ha existido durante mucho tiempo y va fuerte. Los objetos no necesitan ser serializables y no tienes que usar reflexión (aunque XStream sí). XStream puede discernir las variables que apuntan al mismo objeto y no hacer accidentalmente dos copias completas de la instancia. Muchos detalles como ese se han forjado a lo largo de los años. Lo he usado durante varios años y es un ir a. Es tan fácil de usar como puedas imaginar.

new XStream().toXML(myObj)

O

new XStream().fromXML(myXML)

Para clonar,

new XStream().fromXML(new XStream().toXML(myObj))

Más sucintamente:

XStream x = new XStream();
Object myClone = x.fromXML(x.toXML(myObj));
 5
Author: Ranx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-28 03:18:15

Depende.

Para la velocidad, utilice DIY. Para a prueba de balas, utilice la reflexión.

POR cierto, la serialización no es lo mismo que refl, ya que algunos objetos pueden proporcionar métodos de serialización reemplazados (readObject/writeObject) y pueden tener errores

 2
Author: Yoni Roit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-28 16:51:49

Recomendaría la forma DIY que, combinada con un buen método hashCode() e equals() debería ser fácil de probar en una prueba unitaria.

 1
Author: Dominik Sandjaja,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-28 16:47:45

Sugiero anular el objeto.clone(), llama a super.clone () primero y luego call ref = ref.clone() en todas las referencias que desee copiar en profundidad. Es más o menos Hágalo usted mismo enfoque, pero necesita un poco menos de codificación.

 1
Author: x4u,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-28 16:48:32

Para objetos complicados y cuando el rendimiento no es significativo utilizo gson para serializar el objeto en texto json, luego deserializar el texto para obtener un nuevo objeto.

Gson que se basa en la reflexión funciona en la mayoría de los casos, excepto que los campos transient no se copiarán y los objetos con referencia circular con causa StackOverflowError.

public static <ObjectType> ObjectType Copy(ObjectType AnObject, Class<ObjectType> ClassInfo)
{
    Gson gson = new GsonBuilder().create();
    String text = gson.toJson(AnObject);
    ObjectType newObject = gson.fromJson(text, ClassInfo);
    return newObject;
}
public static void main(String[] args)
{
    MyObject anObject ...
    MyObject copyObject = Copy(o, MyObject.class);

}
 1
Author: tiboo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-29 07:59:14