Вместо:
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
Я бы предпочел:
public Person clone() {
try {
return (Person) clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException("This should be impossible ...");
}
}
чтобы вызывающие абоненты не обрабатывали исключение, которое никогда не может произойти, и не должны вызывать.
В подходе копирования-конструктора переключение типов лучше обрабатывается полиморфно:
abstract class Person {
...
public abstract Person deepCopy();
}
class Student {
...
public Student deepCopy() {
return new Student(this);
}
}
class Teacher {
...
public Teacher deepCopy() {
return new Teacher(this);
}
}
теперь компилятор может проверить, что вы предоставили глубокую копию для всех подтипов, и вам не нужно ничего приводить.
Наконец, обратите внимание, что подходы клонирования и конструктора копирования имеют одинаковые общедоступные API (независимо от того, называется ли метод clone()
или deepCopy()
, значения не имеет), поэтому используемый вами подход - деталь реализации. Подход конструктора копирования более многословен, поскольку вы предоставляете и конструктор, и метод, вызывающий этот конструктор, но его легче обобщить для общего средства преобразования типов, что позволяет использовать такие вещи, как:
public Teacher(Person p) {
...
say("Yay, I got a job");
}
Рекомендация: используйте clone, если вам нужна только идентичная копия, используйте конструкторы копирования, если вызывающая сторона может запросить экземпляр определенного типа.