Me pregunto si hay una forma recomendada de hacer clones / copias profundas de instancias en Java.
Tengo 3 soluciones en mente, pero me puedo perder algunas, y me gustaría tener tu opinión
editar: incluya la propuesta de Bohzo y refine 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 compruebe que las instancias mutables también se clonen.
pro:
- control de lo que se realizará
-
contras de ejecución rápida :
- tedioso de escribir y mantener
- propenso a errores (falla de copiar / pegar, propiedad faltante, propiedad mutable reasignada)
Usar 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
contras de mantenimiento :
- menos control de lo que sucede
- propenso a errores con objeto mutable si la herramienta de reflexión tampoco clona subobjetos
- ejecución más lenta
Use el marco de clonación:
Use un marco que lo haga por usted, como:
commons-lang SerializationUtils
Java Deep Cloning Library
Dozer
Kryo
pro:
- igual que la reflexión
- más control sobre lo que se clonará exactamente.
contras:
- cada instancia mutable está completamente clonada, incluso al final de la jerarquía
- podría ser muy lenta de ejecutar
Utilice la instrumentación de bytecode para escribir clon en tiempo de ejecución
Se puede usar javassit , BCEL o cglib para generar un clonador dedicado tan rápido como escribir con una mano. ¿Alguien conoce una biblioteca que usa una de estas herramientas para este propósito?
¿Qué me he perdido aquí?
Cuál recomendarías ?
Gracias.
Respuestas:
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
.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 hacer que se implementen
Serializable
, o en los casos en que no desea 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á usando spring y, por lo tanto, tiene esta utilidad en el classpath.
Deliberadamente omití la opción "hágalo usted mismo": las API anteriores proporcionan un buen control sobre qué hacer y qué no clonar (por ejemplo
transient
, usando , oString[] ignoreProperties
), por lo que no es preferible reinventar la rueda.fuente
El libro de Joshua Bloch tiene un capítulo completo titulado "Punto 10: Anular el clon juiciosamente" en el que explica por qué anular el clon en su mayor parte es una mala idea porque la especificación de Java crea muchos problemas.
Él ofrece algunas alternativas:
Use un patrón de fábrica en lugar de un constructor:
Use un constructor de copia:
Todas las clases de colección en Java son compatibles con el constructor de copia (por ejemplo, new ArrayList (l);)
fuente
Copyable
interfaz que contiene ungetCopy()
método. Simplemente use el patrón prototipo manualmente.newInstance()
método y elYum
constructor harían una copia profunda o una copia superficial?Desde la versión 2.07, Kryo admite la clonación superficial / profunda :
Kryo es rápido, en y de su página puede encontrar una lista de compañías que lo usan en producción.
fuente
Use XStream toXML / fromXML en la memoria. Extremadamente rápido y ha existido durante mucho tiempo y se está fortaleciendo. Los objetos no necesitan ser serializables y no debe usar la reflexión (aunque XStream sí). XStream puede discernir variables que apuntan al mismo objeto y no hacer accidentalmente dos copias completas de la instancia. Muchos detalles como ese han sido elaborados 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 te puedas imaginar.
o
Para clonar,
Más sucintamente:
fuente
Para objetos complicados y cuando el rendimiento no es significativo, uso gson para serializar el objeto en texto json, luego deserializar el texto para obtener un nuevo objeto.
gson que basado en la reflexión funcionará en la mayoría de los casos, excepto que los
transient
campos no se copiarán y los objetos con referencia circular con causaStackOverflowError
.fuente
Depende
Para mayor velocidad, usa bricolaje. Para a 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.
fuente
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.
fuente
Sugeriría anular Object.clone (), llamar primero a super.clone () y luego llamar a ref = ref.clone () en todas las referencias que desee copiar en profundidad. Es más o menos el enfoque Hágalo usted mismo , pero necesita un poco menos de codificación.
fuente
Para una clonación profunda, implemente Serializable en cada clase que desee clonar de esta manera
Y luego usa esta función:
Me gusta esto:
Obj newObject = (Obj)deepClone(oldObject);
fuente