Effektives Java.Klonbare Schnittstelle
-
11-12-2019 - |
Frage
Ich habe das Buch „Effective Java“ gelesen und verstehe keinen Absatz, in dem die Clonable-Schnittstelle erklärt wird.Kann mir jemand diesen Absatz erklären:
... Programmierer gehen davon aus, dass sie eine Klasse erweitern und aufrufen
super.clone
Aus der Unterklasse wird das zurückgegebene Objekt eine Instanz der Unterklasse sein.Die einzige Möglichkeit, wie eine Superklasse diese Funktionalität liefern kannsuper.clone
.Wenn eine Klonmethode ein von einem Konstruktor erstellte Objekt zurückgibt, hat sie die falsche Klasse.
Danke.
Lösung
Das sollte ich zunächst einmal anmerken clone
an und für sich ist kaputt, und das ist ein Kopierkonstruktor, wie Sheep(Sheep cloneMe)
ist eine weitaus elegantere Redewendung als clone
, unter Berücksichtigung der Cloneable
Vertrag ist sehr schwach.Das wissen Sie wahrscheinlich bereits, da Sie das Buch lesen, aber es lohnt sich, es hier einzufügen.
Wie auch immer, um die Frage zu beantworten:
Object.clone()
erstellt ein Objekt desselben Typs wie das Objekt, für das es aufgerufen wurde.Aus diesem Grund wird dringend empfohlen, bis zu „kaskadieren“. Object
um das Ergebnis zu erzielen, das Sie zurückgeben möchten.Wenn jemand beschließt, diese Konvention nicht zu befolgen, erhalten Sie am Ende ein Objekt vom Typ der Klasse, die gegen die Konvention verstoßen hat, was eine Vielzahl von Problemen verursachen wird.
Zur Veranschaulichung: Ich habe eine Klasse wie diese
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();
}
}
Plötzlich, wenn ich es tue
WoolySheep dolly = new WoolySheep("Dolly");
WoolySheep clone = (WoolySheep)(dolly.clone()); // error
Ich bekomme eine Ausnahme wegen dem, was ich zurückbekomme dolly.clone()
ist ein Sheep
, kein WoolySheep
.
Andere Tipps
Ich stimme nicht mit @ Corsisis Antwort zu.Seit Java5.0.Java unterstützt den kovariantigen Rückkehrtyp Daher sollte die korrekte Implementierung für Klon () sein: generasacodicetagpre.
Der empfohlene alternative Kopierkonstruktor unterstützt keinen Polymorphismus.Erwägen Sie das folgende Beispiel (der Copy Constructor kann nicht tun): generasacodicetagpre.
class A {
protected Object clone() {
return new A();
}
}
class B extends A implements Cloneable {
public Object clone() {
return super.clone();
}
}
Hier, A
hat eine ungültige Implementierung von clone
weil dies eine Ausnahme auslösen wird:
B obj = (B)(new B()).clone();
Stattdessen, A.clone()
muss anrufen super.clone()
anstelle eines Konstruktors. Object.clone()
generiert dann ein neues Objekt vom Laufzeittyp anstelle des Kompilierzeittyps.
Alle Felder werden dann auf dieses neue Objekt geklont.Es wäre verlockend, einen Konstruktor zu verwenden, wenn Sie bereits einen haben, der alle Ihre Felder initialisiert (wie einen Kopierkonstruktor), aber das würde zu falschem Verhalten für alle Unterklassen führen.
Wenn die Klasse ist final
, dann spielt es keine Rolle, da es keine Unterklassen haben kann.