2009-04-29 28 views
9

Érase una vez había una clase:Clonación con los genéricos

public class Scope<C extends Cloneable & Comparable<C>> implements Comparable<Scope<C>>, Cloneable, Serializable { 

    private C starts; 
    private C ends; 
    ... 

    @SuppressWarnings("unchecked") 
    @Override 
    public Object clone() { 
     Scope<C> scope; 
     try { 
      scope = (Scope<C>) super.clone(); 
      scope.setStarts((C) starts.clone()); // The method clone() from the type Object is not visible 
      scope.setEnds((C) ends.clone()); // The method clone() from the type Object is not visible 
     } catch (CloneNotSupportedException e) { 
      throw new RuntimeException("Clone not supported"); 
     } 
     return scope; 
    } 
} 

En Objeto tenemos:

protected native Object clone() throws CloneNotSupportedException; 

Y Cloneable interfaz es:

public interface Cloneable { 
} 

Cómo debería clonar ¿esta?

+0

yo no entiendo muy bien la pregunta. ¿Ya no tiene Scope un método clone()? –

+2

Los genéricos no juegan en este problema. ¿Qué pasa si 'start' y' ends' son un tipo específico que implementó 'Cloneable', pero no amplió la accesibilidad a" public ". Tendría el mismo problema. – erickson

Respuesta

10

Creo que la respuesta verde actual es mala, ¿por qué podría preguntar?

  • Se añade una gran cantidad de código
  • Se requiere que enumerar todos los campos que va a copiar y hacer esto
  • Esto no funcionará para las listas al utilizar clone() (Esto es lo clone() para HashMap dice: Devuelve una copia superficial de esta instancia de HashMap: las claves y los valores propios no están clonados.) Por lo que terminan haciendo de forma manual (esto me hace llorar)

Ah, y por cierto también serialización es mala, es posible que tenga que añadir Serializable por todo el lugar (esto también me hace llorar).

Entonces, ¿cuál es la solución:

biblioteca de Java profundas clonación La biblioteca de clonación es una biblioteca de Java fuente pequeña y abierta (licencia Apache) que en el fondo clones de objetos. Los objetos no tienen que implementar la interfaz Cloneable. Efectivamente, esta biblioteca puede clonar CUALQUIER objeto Java. Se puede utilizar, es decir, en implementaciones de caché, si no desea que se modifique el objeto en caché o siempre que desee crear una copia profunda de los objetos.

Cloner cloner=new Cloner(); 
XX clone = cloner.deepClone(someObjectOfTypeXX); 

Compruébelo usted mismo en http://code.google.com/p/cloning/

+2

Una operación de clonación semánticamente correcta debe reemplazar todos los campos que encapsulan el estado de un objeto mutable con una copia de ese objeto, pero * no * debe hacerlo con campos que encapsulan la * identidad * de un objeto (objetos que contienen campos que encapsulan tanto el estado mutable como la identidad del objetivo no pueden clonarse correctamente de forma aislada, puede ser posible clonar un grupo de tales objetos, pero hacerlo correctamente requiere una comprensión de la relación entre ellos). Si un objeto nunca será modificado, independientemente de si el tipo es inmutable, la clonación profunda ... – supercat

+2

... será un desperdicio pero (a menos que la identidad del objeto sea importante) no afectará la corrección.No estoy del todo claro qué tan útil puede ser una utilidad de clonación profunda en general a menos que se use con clases que etiqueten los campos que necesitan copiarse, ya que la necesidad de clonar los campos es una función de cómo se usan los campos, y no el tipo de eso – supercat

+0

Estado vs. Identidad ... Ese es el problema ... –

4

Esta es una razón por la cual no one likes Cloneable. Se supone que es una interfaz de marcador, pero básicamente es inútil porque no se puede clonar un objeto arbitrario Cloneable sin reflejo.

casi la única manera de hacer esto es crear su propia interfaz con un método público clone() (que no tiene que ser llamado "clone()"). Here's an example de otra pregunta de StackOverflow.

+0

Especifica un comportamiento de implementación (no es bueno). Separar esa basura de una interfaz de métodos probablemente no sea una mala idea. –

-1

Como se ve, si una clase trata de implementar Cloneable y desea un clon profunda, entonces todos sus objetos constituyentes tiene que ser inmutable, primitiva, o la necesidad de ser también Cloneable.

A menudo, un enfoque mejor y más fácil es crear un constructor de copia.

public class Scope<C extends Comparable<C>> implements Comparable<Scope<C>>, Serializable { 
    private C starts; 
    private C ends; 
    public Scope(final Scope original) { 
     starts = new C(original.starts); 
     ends = new C(original.ends); 
     // initialize all my other fields from "original" 
    } 
} 

y por supuesto se necesita un constructor de copia en C que es capaz de manejar polimorfismo.

Si no tiene acceso o la capacidad de modificar la fuente a C, cualquier método de copia, sin importar el método, será muy difícil y potencialmente imposible. Por ejemplo, no es posible hacer una copia de una instancia de enum.

+0

Sí, pero la pregunta es cómo clonar el parámetro genérico. No hay forma de especificar que un tipo debe tener un determinado constructor. –

+0

Cuando lo hago, tendré 2 referencias al mismo objeto de inicio ... ¿No es así? – Etam

+0

@etam: por supuesto que no. Es por eso que se llama constructor * copy *. – Eddie

2

Ligeramente OT, pero usted podría ahorrarse mucho futuro con esta pena:

catch (CloneNotSupportedException e) { 
     throw new RuntimeException("Clone not supported", e); 
    } 

Así que cuando se obtiene un seguimiento de la pila usted sabe qué objeto que causó el problema.

Para responder a la pregunta principal, su propia interfaz que implementa pública clone() como mmyers escribió y requiere que C extienda eso también.

+0

No es una buena idea. Me gustaría usar Date, por ejemplo. No quiero extender todas las clases que uso. – Etam

+0

Si desea esto para tipos que no controla, la reflexión es su única opción. – Yishai

1

Como comentario general, evite usar Object.clone() siempre que sea posible. Si tiene control sobre el código en cuestión, implemente un constructor de copia. Consulte here para obtener información.

+0

+1. Eso es lo que dije. – Eddie

+1

Sí, pero no hay forma de usar una interfaz para especificar un constructor de copia. Eso significa que tienes que saber realmente el tipo de objeto para copiarlo. –

+1

@mmyers: Algo así como cierto; la alternativa es la reflexión. Sin embargo, con Cloneable tienes otras restricciones que pueden ser igual de difíciles. – Eddie

2

Espero haber resuelto el problema de la clonación genérico en Java:

public class Generic<T> { 
    private T data; 

    public Generic() { 
    // ... 
    } 

    @SuppressWarnings("unchecked") 
    @Override 
    public Object clone() { 
    Generic<T> cloned = new Generic<T>(); 
    try { 
     cloned.data = (T) data.getClass().getMethod("clone").invoke(data); 
    } catch (Exception e) { 
     // ... 
    } 
    return cloned; 
    } 
}