patterns pattern patrones method diseño book java design

patrones - java design patterns github



¿Por qué#clone() no está en la interfaz Cloneable? (3)

Estaba leyendo sobre la realización de una copia profunda de una matriz correctamente, sin embargo, estaba confundido acerca de cómo se implementa #clone() . Es miembro de la clase java.lang.Object y, sin embargo, si lees los javadocs:

Primero, si la clase de este objeto no implementa la interfaz Cloneable, se lanza una CloneNotSupportedException.

Entonces, ¿por qué definir el método de clone allí en primer lugar? Sin duda, si un método solo puede usarse cuando hay una interfaz presente, pondría el método en la interfaz. La interfaz Cloneable está vacía; es solo una interfaz de marcador utilizada por Java para garantizar que el uso del método de clone sea ​​legal.

Hacerlo de esta manera también elimina la capacidad de hacer uso de los genéricos para garantizar la seguridad del tipo:

class Foo implements Cloneable { // Valid. @Override public Object clone() throws CloneNotSupportedException { // ... } } class TypeSafeFoo implements Cloneable<TypeSafeFoo> { // Not valid. @Override public TypeSafeFoo clone() throws CloneNotSupportedException { // ... } }

¿Por qué Java lo ha hecho de esta manera? Estoy seguro de que tienen razones legítimas, pero parece que no puedo resolverlo.


El contrato de clonación en Java dicta que cada implementación de clone debe primero obtener la instancia clonada de super.clone() . Esto crea una cadena que siempre termina con la llamada a Object.clone , y ese método contiene un código de nivel nativo "mágico" que hace una copia binaria de la struct bruto subyacente que representa el objeto de Java. Si este mecanismo no existiera, el clone dejaría de ser polimórfico: el método Object.clone produce una instancia de la clase a la que se llama; esto no se puede reproducir sin código nativo.

Esta es la razón por la cual el método Object.clone no podría haberse evitado. Cloneable podría haber contenido un método de clone , pero crearía problemas con respecto a la cláusula throws . Tal como está, eres libre de declarar el clone sin excepciones declaradas, o de declarar excepciones arbitrarias. Esta flexibilidad no sería posible si el método ya se hubiera declarado en la interfaz.

Tenga en cuenta que los genéricos serían de poca utilidad para la clonación: imagínese protected T clone() en Object : ¿de dónde provendría T ? ¿Necesitaríamos Object<T> y obligaríamos a todas y cada una de las clases en el universo de Java a parametrizarse sobre sí mismas, y todo esto solo para hacer que este mecanismo semi-obsoleto funcione un poco mejor? Tenga en cuenta también que este código es perfectamente legal:

public class TheMightyOne implements Cloneable { @Override public TheMightyOne clone() { return (TheMightyOne) super.clone(); } }

Puedes llamarlo:

TheMightyOne one = new TheMightyOne(); TheMightyOne two = one.clone(); // do downcasts needed


Para hacer frente a la creación del clon y la copia de campo básica, el clon necesita heredar una implementación de método. Una clase clonable puede aparecer en cualquier parte de la jerarquía y es posible que necesite extender una superclase específica para realizar su trabajo principal. La única superclase desde la que todas las clases clonables posibles pueden heredar la implementación es Object.

La clonación se definió mucho antes que los genéricos.


clone() método clone() es innecesario, cada objeto podría invocar Object.clone método Object.clone por Object.clone , por lo que un objeto puede clonarse dependiendo de si se debe implementar la interfaz Cloneable . Esto es por una razón de seguridad. Simplemente puede clonar un objeto que implementa Cloneable con esta utilidad:

@SuppressWarnings("unchecked") public static <T extends Cloneable> T clone(Cloneable obj) { T rtn = null; try { Method method = Object.class.getDeclaredMethod("clone"); method.setAccessible(true); rtn = (T) method.invoke(obj); } catch (Throwable t) { t.printStackTrace(); } return rtn; }