2009-03-19 4 views
7

Estoy manteniendo una base de código Java anterior (jvm 1.4) que parece utilizar la clonación como una alternativa a la instanciación de objetos, supongo que como una optimización del rendimiento. He aquí un ejemplo artificioso:¿La clonación proporciona una mejora en el rendimiento sobre los métodos de construcción/fábrica?

public class Foo { 
    private SomeObject obj; // SomeObject implements Cloneable 
    public Foo() { 
    obj = new SomeObject(); 
    obj.setField1("abc"); // these fields will have the same value every time 
    obj.setField2("def"); 
    } 
    public void doStuff() { 
    SomeObject newObj = obj.clone(); // clone it instead of using a factory method 
    // do stuff with newObj 
    } 
} 

Las advertencias habituales acerca de la optimización prematura no obstante, era esto realmente un lenguaje recomendado en algún momento?

+0

Parece extraño. Usaría el constructor para la nueva instanciación de objetos. –

Respuesta

3

Una razón para invocar clone() en lugar del constructor de copia o un método de fábrica es que ninguna de las otras opciones puede estar disponible.

Implementar clone() para realizar una copia de objeto superficial (la copia profunda es más complicada) es trivial en comparación con implementar un constructor de copia o un método de fábrica para realizar la misma operación. Para implementar clone(), una clase necesita simplemente implementar la interfaz Cloneable y reemplazar el método clone() con uno que invoca super.clone() que generalmente invoca Object.clone(). Object.clone() copia cada propiedad del objeto original en la propiedad correspondiente del duplicado, creando así una copia poco profunda.

Aunque la implementación de clone() es sencilla, todavía es fácil olvidarse de implementar Cloneable. En consecuencia, un riesgo potencial de usar clone() para duplicar un objeto es que si la clase de ese objeto no implementa Cloneable y clone() invoca Object.clone() directa o indirectamente, lanzará CloneNotSupportedException.

Consulte este code example y un anterior discussion en el poor design de la interfaz Cloneable.

4

Presumiblemente querían una copia. Quizás quieran pasarlo a otra función, y no pueden estar seguros de que esa función no lo cambie. Es una forma de asegurarse de que el método doStuff() esté relacionado con el estado del objeto Foo al que se llama.

+0

Combinando esto arriba +1 porque (después de mirar el código) aparece o es más probable que aplique que mi respuesta más esotérica. – MarkusQ

+0

Tenga en cuenta que este es el objetivo principal de la clonación (suponiendo que mis profesores sabían de lo que estaban hablando). – Chris

+0

+1: Sí, parece que aquí '.clone()' es la forma en Java de pasar por valor. –

1

Puede ser una optimización del rendimiento, dependiendo de cuánto trabajo se haga en los constructores.

Es más probable que se use porque la semántica es diferente. La clonación proporciona una forma de implementar "semántica prototipo" (como en javascript, self, etc.) en un lenguaje que normalmente no tiende de esa manera.

+0

¿Cómo es eso? Pensé que la semántica del prototipo significaba que podrías alterar el comportamiento del constructor u otros campos o métodos en tiempo de ejecución. –

+0

La clonación le permite establecer valores iniciales, etc. y (al usar delegados) cambiar el comportamiento también. Es una especie de kludge, pero por lo general no tienes una semántica propia, solo un poco de jugo, por lo que a menudo funciona en la práctica. – MarkusQ

0

Si el constructor SomeObject realiza un trabajo costoso, como tomar algo de una base de datos o analizar algo, o leer algo de un archivo, entonces el clon tendría sentido para evitar hacer el trabajo.

Si el constructor no hace nada, entonces realmente no hay necesidad de usar el clon.

Editar: se ha añadido código para mostrar que el clon no tiene que hacer el mismo trabajo que el constructor:

class Main 
    implements Cloneable 
{ 
    private final double pi; 

    public Main() 
    { 
     System.out.println("in Main"); 
     // compute pi to 1,000,000,000 decimal palaces 
     pi = 3.14f; 
    } 

    public Object clone() 
    { 
     try 
     { 
      return (super.clone()); 
     } 
     catch(final CloneNotSupportedException ex) 
     { 
      throw new Error(); // would not throw this in real code 
     } 
    } 


    public String toString() 
    { 
     return (Double.toString(pi)); 
    } 

    public static void main(String[] args) 
    { 
     final Main a; 
     final Main b; 

     a = new Main(); 
     b = (Main)a.clone(); 

     System.out.println("a = " + a); 
     System.out.println("b = " + b); 
    } 
} 

El Constructor principal se llama una vez, el PI cómputo se realiza una vez.

+0

si va a marcar algo, ¡al menos comente lo que cree que es impreciso! – TofuBeer

+0

Si el constructor de copias hizo un trabajo costoso, clon tendría que hacer lo mismo. –

+0

clon puentea el constructor. ¿Cómo se obtiene tendría que hacer lo mismo? – TofuBeer

2

Un problema importante con los constructores de copia es que el tipo de objeto debe conocerse en tiempo de compilación.Si una clase heredable admite un constructor de copia, y al constructor se le pasa un objeto de clase derivada, el constructor producirá un objeto de clase base cuyas propiedades de clase base generalmente coinciden con las del objeto pasado, pero el nuevo objeto ganó ' t admite las características que estaban presentes en el objeto pasado que no estaban presentes en la clase base.

Es posible resolver este problema un tanto haciendo un constructor de copia "protegido" y tener un método de copiado de fábrica sobresaturado en cada clase derivada que llama al propio constructor de copia de esa clase, que a su vez llama al constructor de copia de su base clase. Sin embargo, cada clase derivada necesitará un constructor de copia y una anulación del método de copia, agregue o no nuevos campos. Si la clase de caso usa "clonar", este código adicional puede eliminarse.

Cuestiones relacionadas