Pergunta

Eu li o livro Effective Java e não entendo um parágrafo onde é explicada a interface Clonable.Alguém pode me explicar este parágrafo:

...os programadores assumem que se estenderem uma classe e invocarem super.clone Da subclasse, o objeto retornado será uma instância da subclasse.A única maneira de uma superclasse fornecer essa funcionalidade é retornar um objeto obtido chamando super.clone.Se um método clone retornar um objeto criado por um construtor, ele terá a classe errada.

Obrigado.

Foi útil?

Solução

Devo observar para começar com isso clone por si só está quebrado, e que um construtor de cópia, como Sheep(Sheep cloneMe) é uma expressão muito mais elegante do que clone, considerando o Cloneable contrato é muito fraco.Você provavelmente já sabe disso, pois está lendo o livro, mas vale a pena colocar aqui.

De qualquer forma, para responder à pergunta:

Object.clone() criará um objeto do mesmo tipo do objeto em que foi chamado.Por esta razão, é fortemente encorajado a "cascata" até Object para obter o resultado que você planeja retornar.Se alguém decidir não seguir esta convenção, você acabará com um objeto do tipo da classe que quebrou a convenção, o que causará uma infinidade de problemas.

Para ilustrar eu tenho uma aula assim

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, se eu fizer

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

Vou receber uma exceção porque o que recebo de volta dolly.clone() é um Sheep, não um WoolySheep.

Outras dicas

Não concordo com a resposta de @corsiKa.desde Java5.0.Java suporta o tipo de retorno covariante, portanto, a implementação correta para clone() deve 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.
    }
}

Além disso, o construtor de cópia alternativo sugerido não suporta polimorfismo.considere o seguinte exemplo (que o construtor de cópia não pode fazer):

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();
    }
}

Aqui, A tem uma implementação inválida de clone porque isso lançará uma exceção:

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

Em vez de, A.clone() deve ligar super.clone() em vez de um construtor. Object.clone() gerará então um novo objeto do tipo de tempo de execução em vez do tipo de tempo de compilação.

Todos os campos são então clonados neste novo objeto.Seria tentador usar um construtor se você já tiver um que inicialize todos os seus campos (como um construtor de cópia), mas isso resultará em comportamento incorreto para qualquer subclasse.

Se a aula for final, então não importa, pois não pode ter nenhuma subclasse.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top