Совершенно верно, что Object.clone()
делает несколько вещей, которые просто не могут быть достигнуты в Java.
С Джош Блох о дизайне: конструктор копирования и клонирование (выделено):
Метод клонирования объекта очень сложен. Он основан на полевых копиях и является «экстралингвистическим». Он создает объект без вызова конструктора. Нет гарантий, что он сохранит инварианты, установленные конструкторами.
Object.clone()
делает то, что не разрешено языком. Вот почему, среди многих других причин, clone()
не работает.
(Если вы еще этого не сделали, вам также следует прочитать его книгу Эффективная Java , чтобы понять, почему он (и многие другие) считают, что Java clone()
и Cloneable
не работает).
Если вы просто хотите создать объект того же класса, что и другой произвольный объект, то это на самом деле вполне достижимо с некоторой оговоркой (а именно, что не все типы являются общедоступными для создания экземплярами) с помощью отражения.
Вот пример того, как использовать отражение для:
- Узнать класс объекта во время выполнения
- Список объявленных им полей, методов и конструкторов
- Найти его конструктор копирования (если есть) и пытается вызвать его, используя заданный объект как
параметр.
import java.lang.reflect.*;
public class NewInstance {
static void print(String label, Object[] arr) {
System.out.println(label);
for (Object o : arr) {
System.out.println(o);
}
System.out.println("---");
}
static Object newInstance(Object o) {
Class<?> c = o.getClass();
System.out.println("Class is " + c);
print("FIELDS:", c.getDeclaredFields());
print("METHODS:", c.getDeclaredMethods());
print("CONSTRUCTORS:", c.getDeclaredConstructors());
try {
Constructor<?> cc = c.getDeclaredConstructor(c);
o = cc.newInstance(o);
} catch (NoSuchMethodException e) {
System.out.println("No copy constructor found!");
} catch (IllegalAccessException e) {
System.out.println("Copy constructor inaccessible!");
} catch (InstantiationException e) {
System.out.println("Instantiation failed!");
} catch (InvocationTargetException e) {
System.out.println("Copy constructor threw " + e.getCause());
}
return o;
}
public static void main(String args[]) {
Object o1 = "hello";
Object o2 = newInstance(o1);
boolean success = (o1 != o2) && (o1.equals(o2));
System.out.println("Attempt " + (success ? "succeeded!" : "failed :("));
}
}
Выход:
Class is class java.lang.String
FIELDS:
// (omitted)
METHODS:
// (omitted)
CONSTRUCTORS:
public java.lang.String()
public java.lang.String(java.lang.String) // this is what we're looking for!
// (rest omitted)
---
Attempt succeeded!
Обратите внимание, что это всего лишь пример, показывающий, что тип можно проверить во время выполнения, а также можно найти и вызвать конструктор копирования. Таким образом, он не работает, если o
является ArrayList
, потому что у него нет конструктора, который принимает ArrayList
(у него есть конструктор, который принимает Collection
, что ArrayList
).
Я оставлю это вам в качестве упражнения о том, как расширить поиск конструктора копирования, чтобы включить эти совместимые перегрузки.