Есть два популярных подхода. Одним из них является предоставление clone
метода, как вы упомянули, вот так.
public class C implements Cloneable {
@Override public C clone() {
try {
final C result = (C) super.clone();
// copy fields that need to be copied here!
return result;
} catch (final CloneNotSupportedException ex) {
throw new AssertionError();
}
}
Обратите внимание на "скопировать поля ... здесь!" часть. Начальная result
является только поверхностной копией, что означает, что при наличии ссылки на объект и оригинал, и result
будут использовать один и тот же объект. Например, если C
содержит private int[] data
, вы, вероятно, захотите скопировать это.
...
final C result = (C) super.clone();
result.data = data.clone();
return result;
...
Обратите внимание, что вам не нужно копировать примитивные поля, поскольку их содержимое уже скопировано, или неизменяемые объекты, поскольку они не могут измениться в любом случае.
Второй подход - предоставить конструктор копирования.
public class C {
public C(final C c) {
// initialize this with c
}
}
Или копировальная фабрика.
public class C {
public static C newInstance(final C c) {
return new C(c);
}
private C(final C c) {
// initialize this with c
}
}
Оба подхода имеют свои соответствующие свойства. clone
хорошо, потому что это метод, поэтому вам не нужно знать точный тип. В конце концов, вы всегда должны получить «идеальную» копию. Конструктор копирования хорош, потому что у вызывающей стороны есть возможность принять решение, как видно из Коллекций Java.
final List c = ...
// Got c from somewhere else, could be anything.
// Maybe too slow for what we're trying to do?
final List myC = new ArrayList(c);
// myC is an ArrayList, with known properties
Я рекомендую выбрать любой подход, какой вам больше подходит.
Я бы использовал другие подходы, такие как рефлексивное копирование или немедленная сериализация / десериализация, только в модульных тестах. Мне они кажутся менее подходящими для производственного кода, в основном из-за проблем с производительностью.