2009-11-26 13 views
6

Es demasiado tarde para cambiar la pregunta, pero más preciso habría sido preguntar "¿Por qué clone() no permite singletons?". Un método copy() sería más conveniente.¿Por qué las enumeraciones de Java no son clonables?


¿Hay alguna razón por la cual las enumeraciones en Java no se pueden clonar?

El manual indica que

Esto garantiza que las enumeraciones no son clonados, que es necesario para preservar su estado "Singleton".

Pero devolver la instancia también también conservaría su estado, y podría manejar enumeraciones asociadas de la misma forma que otros objetos clonable.

Se podría argumentar que

La intención general [del clon()] es que, para cualquier objeto x, la expresión: x.clone() != x será cierto, [...]

Pero para los singleton, por el contrario, quiero que x.clone() == x sea cierto. Si la instancia en sí misma fuera devuelta, entonces el patrón singleton sería transparente para los objetos de referencia.

¿Por qué las enumeraciones no pueden clonarse o se olvidaron de pensar en singletons e inmutables, cuando se especificó clone()?

+0

Con enumeraciones, ¿qué hay para clonar? – omerkudat

Respuesta

5

Pero para embarazos únicos, por el contrario quiero x.clone() == x para ser verdad.

Usted puede desear, pero creo que es raro que el código siguiente se rompería:

interface Foo extends Cloneable { public int getX(); public void setX(int x); } 
enum FooSingleton implements Foo { 
    INSTANCE; 
    private int x; 
    public int getX(){ return x; } 
    public void setX(int x){ this.x = x; } 
} 
class FooClass implements Foo { 
    private int x; 
    public int getX(){ return x; } 
    public void setX(int x){ this.x = x; } 
} 

boolean omg(Foo f){ 
    Foo c = f.clone(); 
    c.setX(c.getX() + 1); 
    return c.getX() != f.getX(); 
} 
assert omg(new FooClass());  // OK 
assert omg(FooSingleton.INSTANCE); // WTF? 

(Por supuesto, ya clone() sólo da copias de poca profundidad, incluso una aplicación correcta del fuere causa errores en el código anterior.)

Por otro lado, puedo aceptar que tendría sentido para las operaciones de clonación solo return this para objetos inmutables, y las enumeraciones realmente deberían ser inmutables. Ahora, cuando se escribió el contrato para clone(), aparentemente no pensaban en inmutables, o no querían un caso especial para un concepto que no es compatible con el idioma (es decir, tipos inmutables).

Y así, clone() es lo que es, y no se puede cambiar algo que ha existido desde Java 1.0. Estoy bastante seguro de que en algún lugar, hay un código que se basa totalmente en clone() devolviendo un objeto nuevo y distinto, tal vez como una clave para un IdentityHashMap o algo así.

1

Supongo que no querían tratar los singleton como un caso especial cuando se especificó clone(). Eso habría complicado la especificación. Así que ahora los desarrolladores de la biblioteca tienen que tratarlos como un caso especial, pero para el resto de nosotros, es bueno que podamos confiar en ese x.clone() != x.

6

Si su método de clonación devuelve this instancia en lugar de un objeto distinto, entonces no es un clon, ¿verdad?

The Javadoc dice:

Por convención, el objeto devuelto por este método debe ser independiente de este objeto (que está siendo clonado).

No se supone que los clics se clonen porque se supone que solo debe haber una instancia de cada valor.

EDIT: En respuesta al comentario siguiente:

eso es exactamente lo que critico. ¿Por qué no devuelve la misma instancia, si hay no puede ser diferente?

Porque realmente no tiene sentido. Si es el mismo objeto, entonces no es un clon.El Javadocs también dicen:

La intención general es que, para cualquier objeto x, la expresión:

x.clone() != x
será verdad, y que la expresión :
x.clone().getClass() == x.getClass()
será cierto, pero estos no son requisitos absolutos .

Así que la intención es que el método clone() devuelva un objeto distinto. Lamentablemente, dice que no es un requisito absoluto, lo que hace que su sugerencia sea válida, pero sigo pensando que no es sensato porque no es útil tener un método de clonación que devuelva this. Incluso podría causar problemas si estuviera haciendo algo dudoso como tener un estado mutable en sus constantes enum o sincronizar en ellos. El comportamiento de dicho código sería diferente según si el método de clonación realizó la clonación adecuada o simplemente devolvió this.

Realmente no explica por qué quiere tratar enumeraciones como Cloneable cuando son intrínsecamente no clonables. Querer tener un método de clonación que no obedezca las convenciones aceptadas parece un truco para resolver un problema más fundamental con su enfoque.

+0

Porque el "contrato" para el método 'clone' es devolver una copia. –

+1

@Stephen C: sí, pero no todos los objetos deben cumplir ese contrato, es decir, no todas las clases deberían implementar Cloneable. –

+0

Creo que el Javadoc que ha citado realmente admite OPs point: el 99% de todos los programadores Java pensarán en la independencia en términos de estado del objeto, es decir, modificar el estado de 'x' no afectará el estado de (el resultado de anterior) 'x.clone()'. Y en este sentido, es * muy * razonable implementar 'clone()' por 'return this'. Me atrevería a decir que cualquier código que dependa de la no identidad del resultado 'clone()' es mucho más probable que se rompa que el código que transfiere ** instancias ** ** singleton ** inmutables a (terceros?) métodos que intentan 'clonar()', por ejemplo para almacenar el estado del objeto. – misberner

8

¿Cuál es el propósito de clonar un singleton, si x.clone() == x? ¿No puedes simplemente usar x de inmediato?

En sentido estricto, si se desea clonar algo y cumplir x.clone() == x, el único objeto que puede ser el resultado del clon es x sí:

def clone() { 
    return this; 
} 

Cuál puede ser engañosa ...


Si está diseñando algo y se basan en clone() para la diferenciación, lo estás haciendo mal ... en mi humilde opinión

+0

@Christian, ¿tu código trata con otros objetos que no implementan Cloneable o todo tiene que ser clonable? –

1

Tu propia respuesta a tu pregunta es la mejor. En general, las personas esperan clone() para devolver un objeto diferente. La semántica de Cloneable tiene más sentido de esa manera. ("El objeto es clonable ... oh, debo ser capaz de hacer copias.") No puedo pensar en una situación improvisada donde eso importa, pero ese era el significado semántico previsto de Cloneable.

Creo que incluso si estuvieran pensando en singletons, no lo habrían cambiado. Después de todo, es responsabilidad del programador decidir qué se puede clonar y qué no, al agregar selectivamente (y potencialmente anular) la interfaz Cloneable, y la mayoría de los programadores no agregarán la interfaz Cloneable a singletons tampoco.

+0

Para objetos inmutables, literalmente, la única diferencia entre implementar 'clone()' correctamente y simplemente 'return this' es el resultado de' x.clone()! = X', del que no tiene ningún motivo real para preocuparse. Si se encuentra en una situación en la que podría tener un objeto mutable o inmutable, simplemente haga una copia real. Las copias de objetos inmutables son baratas: no tiene que volver a recurrir en el gráfico de objetos, simplemente asigne los campos para que sean iguales. – Kevin

0

Pero para los singletons por el contrario quiero x.clone() == x para ser verdad.

No, eso no sería un clon. Así, por únicos, desea que esta:

public Object clone() throws CloneNotSupportedException { 
    throw new CloneNotSupportedException(); 
} 
+1

Sí, deberías. Un clon ** debe ** devolver un clon que no tiene sentido para un singleton. –

+0

¿Por qué querrías hacer público 'clone'? –

+1

Quizás porque esta es la convención al anular 'clone()', ver http://java.sun.com/javase/6/docs/api/java/lang/Cloneable.html. –

Cuestiones relacionadas