¿Por qué el método clone () está protegido en java.lang.Object?

112

¿Cuál es la razón específica que clone()se define como protegida en java.lang.Object?

Alex N.
fuente

Respuestas:

107

El hecho de que el clon esté protegido es extremadamente dudoso, al igual que el hecho de que el clonemétodo no esté declarado en la Cloneableinterfaz.

Hace que el método sea bastante inútil para tomar copias de datos porque no puede decir :

if(a instanceof Cloneable) {
    copy = ((Cloneable) a).clone();
}

Creo que el diseño de Cloneableahora se considera en gran medida un error (cita a continuación). Normalmente me gustaría poder hacer implementaciones de una interfaz Cloneablepero no necesariamente hacer la interfazCloneable (similar al uso de Serializable). Esto no se puede hacer sin reflexión:

ISomething i = ...
if (i instanceof Cloneable) {
   //DAMN! I Need to know about ISomethingImpl! Unless...
   copy = (ISomething) i.getClass().getMethod("clone").invoke(i);
}

Cita de Effective Java de Josh Bloch :
"La interfaz Cloneable fue pensada como una interfaz mixin para que los objetos anunciaran que permiten la clonación. Desafortunadamente, no cumple con este propósito ... Este es un uso altamente atípico de interfaces y no uno para ser emulado ... Para que la implementación de la interfaz tenga algún efecto en una clase, esta y todas sus superclases deben obedecer un protocolo bastante complejo, inaplicable y en gran parte indocumentado "

oxbow_lakes
fuente
2
Si el objeto no se puede clonar, entonces el objeto clone () lanzará CloneNotSupportedException. Por lo tanto, debe ser clonable si va a llamar a super.clone () (lo que da como resultado que se llame a Object.clone ()). No veo cómo se puede serializar un objeto sin implementar Serializable.
Steve Kuo
1
"Creo que el diseño de Cloneable ahora se considera en gran medida un error". [cita requerida]
Kevin Panko
Lo siento, no estaba insinuando eso. Simplemente estaba insinuando que un "buen" diseño es no hacer que una interfaz se extienda Serializable; depende de las implementaciones decidir si se implementa Serializable. Estaba extendiendo esto a Cloneable, no es algo que una interfaz deba extender, pero la implementación de una interfaz es gratuita Cloneable. El problema es que, si tiene un parámetro del tipo de interfaz, le pregunta si se puede clonar; ¡pero entonces no puedes clonarlo!
oxbow_lakes
6
@Kevin - El eficaz Java pp45 de Josh Bloch . "La interfaz Cloneable fue pensada como una interfaz mixin para que los objetos anuncien que permiten la clonación. Desafortunadamente, no cumple con este propósito"
oxbow_lakes
También en la misma página: "Este es un uso muy atípico de interfaces y no uno para ser emulado" y "Para que la implementación de la interfaz tenga algún efecto en una clase, esta y todas sus superclases deben obedecer a una bastante compleja, protocolo inaplicable y en gran parte indocumentado "
oxbow_lakes
30

La interfaz Clonable es solo un marcador que dice que la clase puede admitir la clonación. El método está protegido porque no debe llamarlo en el objeto, puede (y debe) anularlo como público.

Desde el sol:

En la clase Object, el método clone () se declara protegido. Si todo lo que hace es implementar Cloneable, solo las subclases y los miembros del mismo paquete podrán invocar clone () en el objeto. Para permitir que cualquier clase en cualquier paquete acceda al método clone (), deberá anularlo y declararlo público, como se hace a continuación. (Cuando anula un método, puede hacerlo menos privado, pero no más privado. Aquí, el método clone () protegido en Object se anula como método público).

Bill K
fuente
Lo cual está bien, hasta que incorpore interfaces a la mezcla ; intente clonar una implementación desconocida deSet
oxbow_lakes
@oxbow_lakes: pero tal vez algunas implementaciones de Set no se pueden clonar
newacct
3
No se puede clonar nada que no implemente la interfaz Clonable, es un marcador que dice "Esta clase es clonable correctamente", muy similar a la interfaz Serializable. Por cierto, hay una forma de clonar clases a través de la serialización que funciona bien: busque en Google algo como "clon de serialización de Java" y probablemente encontrará varias formas de obtener una copia profunda de su objeto.
Bill K
4
No puede clonar nada que no implemente la interfaz Cloneable, pero el hecho de que algo implemente la interfaz Cloneable no significa que pueda clonarlo.
Michael Myers
1
@BuckCherry toString tiene una implementación predeterminada, si la llamas algo bueno sucederá y obtendrás una cadena. equals tiene una implementación predeterminada (igual que ==). El clon no puede tener una implementación predeterminada. Si llama a clone en un objeto que no lo ha implementado, no obtendrá un comportamiento razonable. La clonación es complicada y realmente no se puede hacer automáticamente (algunos objetos sin constructores predeterminados pueden ser imposibles de clonar genéricamente), por lo que, por defecto, lo hacen un poco más seguro. Creo que ponerlo en Object, sin embargo, podría no haber sido necesario.
Bill K
7

cloneestá protegido porque es algo que debe anularse para que sea específico de la clase actual. Si bien sería posible crear un clonemétodo público que clonara cualquier objeto, esto no sería tan bueno como un método escrito específicamente para la clase que lo necesita.

Andrew Hare
fuente
pero ¿por qué hay que protegerlo para eso?
Janusz
3
Está protegido, por lo que no usa el objeto de uno (de todos modos, solo lanzará una excepción). Quieren que lo anule en una clase y luego lo haga público. (respondido un par de veces a continuación también)
Bill K
1
E inútil al considerar interfaces según mi punto a continuación
oxbow_lakes
debería ser anulado no está muy claro, entonces debería haber sido abstracto y luego no en la clase de objeto, estoy tratando de entender por qué es así.
Kumar Abhishek
4

El método Clone no se puede usar directamente en ningún objeto, por lo que está destinado a ser anulado por la subclase.

Por supuesto, podría ser público y lanzar una excepción apropiada cuando la clonación no sea posible, pero creo que sería engañoso.

La forma en que se implementa la clonación en este momento le hace pensar en por qué desea usar la clonación y cómo desea que se clone su objeto.

Silfverstrom
fuente
2

Está protegido porque la implementación predeterminada hace una copia superficial de todos los campos (incluido el privado), evitando el constructor . Esto no es algo para lo que un objeto pueda estar diseñado para manejar en primer lugar (por ejemplo, podría realizar un seguimiento de las instancias de objetos creados en una lista compartida, o algo similar).

Por la misma razón, la implementación predeterminada de clone()arrojará si el objeto al que se llama no se implementa Cloneable. Es una operación potencialmente insegura con consecuencias de gran alcance y, por lo tanto, el autor de la clase debe participar explícitamente.

Pavel Minaev
fuente
En realidad, la implementación predeterminada (en el objeto) arroja una excepción de acuerdo con los documentos ...
Bill K
2
No, no solo lanza. De su JavaDoc: "El método clon para la clase Object realiza una operación de clonación específica. Primero, si la clase de este objeto no implementa la interfaz Cloneable, se lanza una CloneNotSupportedException. Tenga en cuenta que se considera que todas las matrices implementan la interfaz Cloneable. De lo contrario, este método crea una nueva instancia de la clase de este objeto e inicializa todos sus campos con exactamente el contenido de los campos correspondientes de este objeto, como por asignación; los contenidos de los campos no se clonan en sí mismos ".
Pavel Minaev
2

Desde el javadoc de cloneable.

* By convention, classes that implement this interface (cloneable) should override 
* <tt>Object.clone</tt> (which is protected) with a public method.
* See {@link java.lang.Object#clone()} for details on overriding this
* method.

* Note that this interface does <i>not</i> contain the <tt>clone</tt> method.
* Therefore, it is not possible to clone an object merely by virtue of the
* fact that it implements this interface.  Even if the clone method is invoked
* reflectively, there is no guarantee that it will succeed.

Por lo tanto, podría llamar a clonar en cada objeto, pero esto le daría la mayoría de las veces, no los resultados que desea o una excepción. Pero solo se recomienda si implementa cloneable.

Janusz
fuente
2
No puede llamar a clonar en cada Objeto, ¡porque está protegido!
Pavel Minaev
2

En mi humilde opinión, es tan simple como esto:

  • #clone no debe invocarse en objetos no clonables, por lo tanto, no se hace público
  • #clonetiene que ser llamado por las subclases ob Objectque implementan Cloneable para obtener la copia superficial de la clase correcta

¿Cuál es el alcance correcto para los métodos que serán invocables por subclases, pero no por otras clases?

Es protected.

Las clases implementadas, Cloneablepor supuesto, harán público este método para que pueda ser llamado desde otras clases.

Michaela Maura Elschner
fuente
0

El método Clone () tiene una verificación interna 'instancia de Cloneable o no'. Así es como el equipo de Java podría pensar que restringirá el uso indebido del método clone (). El método clone () está protegido, es decir, se accede solo por subclases. Dado que el objeto es la clase principal de todas las subclases, todas las clases pueden usar el método Clone () de hecho si no tenemos la verificación anterior de 'instancia de Cloneable'. Esta es la razón por la que el equipo de Java podría haber pensado en restringir el uso indebido de clone () haciendo que el método clone () marque 'es una instancia de Cloneable'.

Por lo tanto, cualquier clase que implemente clonable puede usar el método clone () de la clase Object.

Además, dado que se hizo protegido, solo está disponible para aquellas subclases que implementan una interfaz clonable. Si queremos hacerlo público, este método debe ser anulado por la subclase con su propia implementación.

SARIKA
fuente
-2

Sí, el mismo problema que conocí. Pero lo resuelvo implementando este código

public class Side implements Cloneable {
    public Side clone() {

        Side side = null;
        try {
            side = (Side) super.clone();
        } catch (CloneNotSupportedException e) {
            System.err.println(e);
        }
        return side;
    }
}

Tal como dijo alguien antes.

fyhao
fuente
1
CloneNotSupportedException es otro ejemplo de una excepción marcada que debe estar desmarcada (es decir, debe extender RuntimeException, no Exception). Aunque el método clone () en la clase Side implementa Cloneable y, por lo tanto, nunca lanzará CloneNotSupportedException, Side.clone () aún debe detectar o declarar la excepción. Esto solo agrega ruido de manejo de excepciones superfluo al método clone ().
Derek Mahar
-2

Bueno, también los desarrolladores de Sun son solo humanos, y de hecho cometieron un gran error al implementar el método de clonación como protegido, ¡el mismo error que implementaron un método de clonación que no funciona en ArrayList! Entonces, en general, existe un malentendido mucho más profundo, incluso de los programadores Java experimentados sobre el método de clonación.

Sin embargo, recientemente encontré una solución rápida y fácil para copiar cualquier objeto con todo su contenido, sin importar cómo esté construido y qué contiene, vea mi respuesta aquí: Error al usar Object.clone ()

marca
fuente
-3

Una vez más, el marco Java JDK muestra un pensamiento brillante:

La interfaz clonable no contiene un "clon T público ();" método porque actúa más como un atributo (por ejemplo, serializable) que permite clonar una instancia.

No hay nada de malo en este diseño porque:

  1. Object.clone () no hará lo que quieras con tu clase personalizada definida.

  2. Si tiene Myclass implements Cloneable => sobrescribe clone () con "public MyClass clone ()"

  3. Si tiene MyInterface amplía Cloneable y algunas MyClasses implementando MyInterface: simplemente defina "public MyInterface clone ();" en la interfaz y cada método que use objetos MyInterface podrá clonarlos, sin importar su clase MyClass.

Víctor
fuente
2
Si su clase es heredada, su implementación de clonación de clase derivada no estará segura hasta que las implementaciones de su clase base proporcionen métodos de clonación seguros. Además, es una condición de diseño poco común tener una interfaz sin método / atributo. Esta interfaz no obliga a la clase a implementar la clonación.
prap19