interfaz - duplicar un objeto en java
Java: soluciĆ³n recomendada para clonar/copiar profundamente una instancia (8)
Me pregunto si hay una forma recomendada de hacer un clon / copia profunda de la instancia en java.
Tengo 3 soluciones en mente, pero puedo echar de menos algunas, y me gustaría tener su opinión
editar: incluir la proposición de Bohzo y refinar la pregunta: se trata más de la clonación profunda que de la clonación superficial.
Hazlo tu mismo:
codifique las propiedades del clon a mano después de las propiedades y verifique que las instancias mutables también estén clonadas.
Pro:
- control de lo que se realizará
- ejecución rápida
contras:
- tedioso para escribir y mantener
- propenso a errores (copia / pega fracaso, propiedad faltante, propiedad mutable reasignada)
Usa 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 hará el trabajo en una línea.
Pro:
- fácil de escribir
- sin mantenimiento
contras:
- Menos control de lo que sucede
- propenso a errores con objeto mutable si la herramienta de reflexión no clona sub objetos también
- ejecución más lenta
Utilice el marco de clonación
Use un marco que lo haga por usted, como:
commons-lang SerializationUtils
Biblioteca de Deep Cloning de Java
Dozer
Kryo
Pro:
- lo mismo que la reflexión
- Más control sobre lo que será exactamente clonado.
contras:
- cada instancia mutable está completamente clonada, incluso al final de la jerarquía
- podría ser muy lento para ejecutar
Usa instrumentación de bytecode para escribir clones en tiempo de ejecución
javassit , BCEL o cglib pueden usarse para generar un clonador dedicado tan rápido como se escribe una mano. Alguien sabe una lib usando una de estas herramientas para este propósito?
Lo que he perdido aquí?
Cuál recomendarías ?
Gracias.
Para la clonación profunda (clona toda la jerarquía de objetos):
commons-lang SerializationUtils - utilizando la serialización - si todas las clases están bajo su control y puede forzar la implementación de
Serializable
.Biblioteca de Deep Cloning 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 hacer que implementen
Serializable
, o en los casos en que no desee implementarSerializable
.
Para la clonación superficial (clona solo las propiedades de primer nivel):
commons-beanutils BeanUtils - en la mayoría de los casos.
Spring BeanUtils : si ya está utilizando la primavera y, por lo tanto, tiene esta utilidad en classpath.
Deliberadamente omite 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.
Depende
Para mayor velocidad, usa DIY. Para prueba de balas, use 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 anulados (readObject / writeObject) y pueden tener errores
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.
El libro de Joshua Bloch tiene un capítulo completo titulado "Elemento 10: Anular el clon juiciosamente" en el que explica por qué la clonación predominante en su mayor parte es una mala idea porque las especificaciones de Java crean muchos problemas.
Él ofrece algunas alternativas:
Use un patrón de fábrica en lugar de un constructor:
public static Yum newInstance(Yum yum);
Use un constructor de copia:
public Yum(Yum yum);
Todas las clases de colección en Java admiten el constructor de copia (por ejemplo, nueva ArrayList (l);)
Para objetos complicados y cuando el rendimiento no es significativo, uso gson para serializar el objeto a texto json, luego deserializo el texto para obtener un nuevo objeto.
gson que, en función de la reflexión, funcionará en la mayoría de los casos, excepto que transient
campos transient
no se copiarán y los objetos con referencia circular con la causa Error
.
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);
}
Recomendaría la forma de bricolaje que, combinada con un buen método hashCode () y equals (), debería ser fácil de probar en una prueba unitaria.
Sugeriría que anule Object.clone (), llame a super.clone () primero y luego llame a ref = ref.clone () en todas las referencias que quiera copiar profundamente. Es más o menos Hazlo tú mismo pero necesita un poco menos de codificación.
Use XStream enXML / fromXML en la memoria. Extremadamente rápido y ha existido por mucho tiempo y va fuerte. Los objetos no necesitan Serializable y no tiene uso de reflejo (aunque XStream sí). XStream puede discernir variables que apuntan al mismo objeto y no accidentalmente hacer dos copias completas de la instancia. Muchos detalles como ese han sido elaborados a lo largo de los años. Lo he usado por un número de años y es un ir a. Es tan fácil de usar como te 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));