Pregunta

Leí el libro Effective Java y no entiendo un párrafo donde se explica la interfaz Clonable.Alguien me puede explicar este párrafo:

...los programadores suponen que si extienden una clase e invocan super.clone Desde la subclase, el objeto devuelto será una instancia de la subclase.La única forma en que una superclase puede proporcionar esta funcionalidad es devolver un objeto obtenido llamando super.clone.Si un método clon devuelve un objeto creado por un constructor, tendrá la clase incorrecta.

Gracias.

¿Fue útil?

Solución

Debo señalar para empezar con eso clone en sí mismo está roto, y que un constructor de copia, como Sheep(Sheep cloneMe) es un modismo mucho más elegante que clone, Considerando el Cloneable El contrato es muy débil.Probablemente ya lo sepas porque estás leyendo el libro, pero vale la pena incluirlo aquí.

De todos modos, para responder a la pregunta:

Object.clone() creará un objeto del mismo tipo que el objeto sobre el que fue llamado.Por este motivo, se recomienda encarecidamente aplicar "en cascada" hasta Object por obtener el resultado que planea regresar.Si alguien decide no seguir esta convención, terminará con un objeto del tipo de clase que rompió la convención, lo que causará multitud de problemas.

Para ilustrar tengo una clase como esta.

class Sheep implements Cloneable {

    Sheep(String name)...

    public Object clone() {
        return new Sheep(this.name); // bad, doesn't cascade up to Object
    }
}

class WoolySheep extends Sheep {

    public Object clone() {
        return super.clone();
    }
}

De repente, si lo hago

WoolySheep dolly = new WoolySheep("Dolly");
WoolySheep clone = (WoolySheep)(dolly.clone()); // error

Recibiré una excepción porque lo que obtengo de dolly.clone() es un Sheep, No un WoolySheep.

Otros consejos

No estoy de acuerdo con la respuesta de @corsiKa.desde Java5.0.Java admite el tipo de retorno covariante, por lo tanto, la implementación correcta para clone() debería ser:

class Sheep implements Cloneable {

    Sheep(String name)...

    public Sheep clone() {
        return new Sheep(this.name);
    }
}

class WoolySheep extends Sheep {

    public WoolySheep clone() {
        return super.clone(); // compile time error, Type miss match.
    }
}

Además, el constructor de copias alternativo sugerido no admite polimorfismo.considere el siguiente ejemplo (que el constructor de copias no puede hacer):

interface Animal implements Cloneable {
  String whatAreYou()
}

class Cat implements Animal {
  String whatAreYou() {
    return "I am a cat";
  }
  Cat clone() {
    return new Cat();
  }
}

class Dog implements Animal{
  String whatAreYou() {
    return "I am a dog";
  }
  Dog clone() {
    return new Dog();
  }
}

class Lib {
  Animal cloneAnimal(Animal animal) {
    return animal.clone();
  }
}
class A {
    protected Object clone() {
        return new A();
    }
}

class B extends A implements Cloneable {
    public Object clone() {
        return super.clone();
    }
}

Aquí, A tiene una implementación no válida de clone porque esto generará una excepción:

B obj = (B)(new B()).clone();

En cambio, A.clone() debe llamar super.clone() en lugar de un constructor. Object.clone() Luego generará un nuevo objeto del tipo de tiempo de ejecución en lugar del tipo de tiempo de compilación.

Luego, todos los campos se clonan en este nuevo objeto.Sería tentador usar un constructor si ya tiene uno que inicialice todos sus campos (como un constructor de copia), pero eso resultará en un comportamiento incorrecto para cualquier subclase.

Si la clase es final, entonces no importa, ya que no puede tener subclases.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top