Necesito implementar un clon profundo en uno de mis objetos que no tiene superclase.
¿Cuál es la mejor manera de manejar el cheque CloneNotSupportedException
lanzado por la superclase (que es Object
)?
Un compañero de trabajo me aconsejó que lo manejara de la siguiente manera:
@Override
public MyObject clone()
{
MyObject foo;
try
{
foo = (MyObject) super.clone();
}
catch (CloneNotSupportedException e)
{
throw new Error();
}
// Deep clone member fields here
return foo;
}
Esto me parece una buena solución, pero quería lanzarlo a la comunidad de StackOverflow para ver si hay otras ideas que pueda incluir. ¡Gracias!
Cloneable
, lanzar un enAssertionError
lugar de un simpleError
es un poco más expresivo.Respuestas:
¿Absolutamente tienes que usar
clone
? La mayoría de la gente está de acuerdo en que Java no funcionaclone
.Josh Bloch sobre diseño: constructor de copias versus clonación
Puede leer más discusiones sobre el tema en su libro Effective Java 2nd Edition, Item 11: Override
clone
juiciosamente . En cambio, recomienda utilizar un constructor de copias o una fábrica de copias.Continuó escribiendo páginas de páginas sobre cómo, si cree que debe hacerlo, debe implementarlo
clone
. Pero cerró con esto:El énfasis era suyo, no mío.
Como dejó en claro que no tiene más remedio que implementar
clone
, esto es lo que puede hacer en este caso: asegúrese de esoMyObject extends java.lang.Object implements java.lang.Cloneable
. Si ese es el caso, puede garantizar que NUNCA contraerá unCloneNotSupportedException
. LanzarAssertionError
como algunos han sugerido parece razonable, pero también puede agregar un comentario que explique por qué nunca se ingresará el bloque de captura en este caso particular .Alternativamente, como también han sugerido otros, quizás pueda implementar
clone
sin llamarsuper.clone
.fuente
super.clone()
dentro de sus métodos de clonación, una subclase generalmente solo tendrá que anularclone()
si agrega nuevos campos cuyo contenido necesitaría ser clonado. Si alguna superclase usa ennew
lugar desuper.clone()
, entonces todas las subclases deben anularclone()
si agregan o no campos nuevos.A veces es más sencillo implementar un constructor de copias:
Le ahorra la molestia de manipular
CloneNotSupportedException
, trabaja confinal
campos y no tiene que preocuparse por el tipo para devolver.fuente
La forma en que funciona su código es bastante similar a la forma "canónica" de escribirlo. Sin
AssertionError
embargo, lanzaría una dentro de la captura. Señala que nunca se debe alcanzar esa línea.fuente
Cloneable
en general es una idea rota, como se explica en Effective Java. El OP ya expresó que tienen que usarCloneable
. Así que no tengo idea de qué otra manera podría mejorarse mi respuesta, excepto quizás eliminarla por completo.Hay dos casos en los que se
CloneNotSupportedException
lanzará:Cloneable
clonando no se implementa (asumiendo que la clonación real eventualmente difiere delObject
método de clonación). Si la clase en la que está escribiendo este método lo implementaCloneable
, esto nunca sucederá (ya que cualquier subclasa lo heredará de manera apropiada).Cloneable
.El último caso no puede ocurrir en su clase (ya que está llamando directamente al método de la superclase en el
try
bloque, incluso si se invoca desde una llamada de subclasesuper.clone()
) y el primero no debería, ya que su clase claramente debería implementarCloneable
.Básicamente, debes registrar el error con seguridad, pero en este caso en particular, solo sucederá si arruinas la definición de tu clase. Por lo tanto, trátelo como una versión verificada de
NullPointerException
(o similar): nunca se lanzará si su código es funcional.En otras situaciones, necesitaría estar preparado para esta eventualidad; no hay garantía de que un objeto dado sea clonable, por lo que al detectar la excepción, debe tomar las medidas adecuadas según esta condición (continuar con el objeto existente, tomar una estrategia de clonación alternativa por ejemplo, serializar-deserializar, lanzar un
IllegalParameterException
si su método requiere el parámetro por clonable, etc., etc.).Editar : Aunque en general debo señalar que sí,
clone()
realmente es difícil de implementar correctamente y es difícil para las personas que llaman saber si el valor de retorno será el que quieren, doblemente cuando se consideran clones profundos vs superficiales. A menudo es mejor evitar todo el asunto por completo y usar otro mecanismo.fuente
super.clone()
.Utilice la serialización para realizar copias en profundidad. Esta no es la solución más rápida, pero no depende del tipo.
fuente
Puede implementar constructores de copia protegidos así:
fuente
clone()
al objeto devuelto por agetMySecondMember()
menos que tenga unpublic clone
método.Por mucho que la mayoría de las respuestas aquí sean válidas, debo decir que su solución también es cómo lo hacen los desarrolladores de la API de Java. (Ya sea Josh Bloch o Neal Gafter)
Aquí hay un extracto de openJDK, clase ArrayList:
Como habrás notado y otros mencionaron,
CloneNotSupportedException
casi no tiene posibilidad de ser lanzado si declaras que implementas laCloneable
interfaz.Además, no es necesario que anule el método si no hace nada nuevo en el método anulado. Solo necesita anularlo cuando necesite realizar operaciones adicionales en el objeto o necesite hacerlo público.
En última instancia, es mejor evitarlo y hacerlo de otra manera.
fuente
fuente
El hecho de que la implementación de Java de Cloneable esté rota no significa que no pueda crear una propia.
Si el propósito real de OP era crear un clon profundo, creo que es posible crear una interfaz como esta:
luego use el constructor de prototipos mencionado antes para implementarlo:
y otra clase con un campo de objeto AClass:
De esta manera, puede clonar fácilmente en profundidad un objeto de la clase BClass sin necesidad de @SuppressWarnings u otro código engañoso.
fuente