Java: solución recomendada para clonación/copia profunda de una instancia
Me pregunto si existe una forma recomendada de realizar una clonación/copia profunda de una instancia en Java.
Tengo 3 soluciones en mente, pero es posible que se me hayan escapado algunas y me gustaría saber tu opinión.
editar: incluya la propuesta de Bohzo y refine la pregunta: se trata más de clonación profunda que de clonación superficial.
Hazlo tu mismo:
Codifique el clon manualmente con las propiedades después de las propiedades y verifique que las instancias mutables también se clonen.
pro:
- control de lo que se realizará
- ejecución rápida
contras:
- tedioso de escribir y mantener
- propenso a errores (fallo al copiar/pegar, propiedad faltante, propiedad mutable reasignada)
Utilice la 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 haga el trabajo en una línea.
pro:
- fácil de escribir
- sin mantenimiento
contras:
- menos control de lo que sucede
- propenso a errores con objetos mutables si la herramienta de reflexión no clona también subobjetos
- ejecución más lenta
Utilice el marco de clonación:
Utilice un marco que lo haga por usted, como:
commons-lang SerializationUtils
Biblioteca de clonación profunda de Java
Dozer
Kryo
pro:
- igual que la reflexión
- más control sobre lo que se clonará exactamente.
Desventajas:
- cada instancia mutable está completamente clonada, incluso al final de la jerarquía
- su ejecución puede ser muy lenta
Utilice instrumentación de código de bytes para escribir clon en tiempo de ejecución
Se pueden usar javassit , BCEL o cglib para generar un clonador dedicado tan rápido como se escribe con una sola mano. ¿Alguien conoce una biblioteca que utiliza una de estas herramientas para este propósito?
¿Qué me he perdido aquí?
Cuál recomendarías ?
Gracias.
Para 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
.Biblioteca de clonación profunda de Java : utilizando la 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 implementarlos
Serializable
, o en los casos en los que no desea implementarlos.Serializable
.
Para clonación superficial (clona solo las propiedades del primer nivel):
commons-beanutils BeanUtils - en la mayoría de los casos.
Spring BeanUtils : si ya está utilizando Spring y, por lo tanto, tiene esta utilidad en el classpath.
Omití deliberadamente la opción "hágalo usted mismo": las API anteriores brindan un buen control sobre qué clonar y qué no (por ejemplo transient
, usando o String[] ignoreProperties
), por lo que no es preferible reinventar la rueda.
El libro de Joshua Bloch tiene un capítulo completo titulado "Ítem 10: Anular la clonación juiciosamente" en el que explica por qué anular la clonación en su mayor parte es una mala idea porque la especificación de Java crea muchos problemas.
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 copias:
public Yum(Yum yum);
Todas las clases de colección en Java admiten el constructor de copia (por ejemplo, new ArrayList(l);)
Desde la versión 2.07 , Kryo admite la 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 puede encontrar una lista de empresas que lo utilizan en producción.
Utilice XStream toXML/fromXML en la memoria. Extremadamente rápido, existe desde hace mucho tiempo y va fuerte. No es necesario que los objetos sean serializables y no es necesario utilizar la reflexión (aunque XStream sí lo hace). XStream puede discernir variables que apuntan al mismo objeto y no hacer accidentalmente dos copias completas de la instancia. Muchos detalles como ese se han ido perfeccionando a lo largo de los años. Lo he usado durante varios años y es una opción. 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));
Para objetos complicados y cuando el rendimiento no es significativo, uso gson para serializar el objeto en texto json y luego deserializar el texto para obtener un nuevo objeto.
gson que se basa en la reflexión funcionará en la mayoría de los casos, excepto que transient
los campos 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);
}