Клонування - це основна парадигма програмування. Той факт, що Java, можливо, багато в чому це реалізувала погано, зовсім не зменшує необхідність клонування. І легко застосувати клонування, яке буде працювати, але якщо ви хочете, щоб воно працювало, неглибоке, глибоке, змішане, що завгодно. Ви навіть можете використовувати ім’я clone для функції і не реалізовувати Cloneable, якщо хочете.
Припустимо, у мене є класи A, B і C, де B і C є похідними від A. Якщо у мене є список об’єктів типу A, такий:
ArrayList<A> list1;
Тепер цей список може містити об’єкти типу A, B або C. Ви не знаєте, якого типу є об’єкти. Отже, ви не можете скопіювати список таким чином:
ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
list2.add(new A(a));
}
Якщо об’єкт насправді типу B або C, ви не отримаєте правильну копію. А що, якщо A абстрактний? Зараз деякі люди пропонують таке:
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));
}
}
Це дуже, дуже погана ідея. Що робити, якщо ви додасте новий похідний тип? Що робити, якщо B або C в іншому пакеті, і у вас немає доступу до них у цьому класі?
Що ви хотіли б зробити, це:
ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
list2.add(a.clone());
}
Багато людей вказали, чому основна реалізація клону Java є проблематичною. Але це легко подолати таким чином:
У класі А:
public A clone() {
return new A(this);
}
У класі B:
@Override
public B clone() {
return new B(this);
}
У класі С:
@Override
public C clone() {
return new C(this):
}
Я не реалізую Cloneable, просто використовуючи одне і те ж ім'я функції. Якщо вам це не подобається, назвіть це інакше.