Klonowanie to podstawowy paradygmat programowania. Fakt, że Java mogła źle zaimplementować go pod wieloma względami, wcale nie zmniejsza potrzeby klonowania. I łatwo jest wdrożyć klonowanie, które będzie działać tak, jak chcesz, płytko, głęboko, mieszane, cokolwiek. Możesz nawet użyć nazwy clone dla funkcji i nie implementować Cloneable, jeśli chcesz.
Załóżmy, że mam klasy A, B i C, gdzie B i C pochodzą od A. Jeśli mam listę obiektów typu A w następujący sposób:
ArrayList<A> list1;
Ta lista może zawierać obiekty typu A, B lub C. Nie wiesz, jakiego typu są te obiekty. Nie możesz więc skopiować listy w ten sposób:
ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
list2.add(new A(a));
}
Jeśli obiekt jest rzeczywiście typu B lub C, nie otrzymasz właściwej kopii. A co, jeśli A jest abstrakcyjne? Niektórzy ludzie zasugerowali to:
ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
if(a instanceof A) {
list2.add(new A(a));
} else if(a instanceof B) {
list2.add(new B(a));
} else if(a instanceof C) {
list2.add(new C(a));
}
}
To bardzo, bardzo zły pomysł. Co się stanie, jeśli dodasz nowy typ pochodny? Co jeśli B lub C znajdują się w innym pakiecie i nie masz do nich dostępu w tej klasie?
Co chciałbyś zrobić, to:
ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
list2.add(a.clone());
}
Wiele osób wskazało, dlaczego podstawowa implementacja klona w Javie jest problematyczna. Ale można to łatwo pokonać w ten sposób:
W klasie A:
public A clone() {
return new A(this);
}
W klasie B:
@Override
public B clone() {
return new B(this);
}
W klasie C:
@Override
public C clone() {
return new C(this):
}
Nie implementuję Cloneable, po prostu używam tej samej nazwy funkcji. Jeśli ci się to nie podoba, nazwij to inaczej.