В случае, если вы можете использовать расширение вместо реализации, то вы могли бы сделать это таким образом:
public interface Thing { ... }
public abstract class Copyable {
public final Copyable copy() {
Copyable clone = createCopy();
if (clone.getClass() != getClass())
throw new RuntimeException("Copy has wrong type!");
return clone;
}
protected abstract Copyable createCopy();
}
И затем использовать его как:
public class Test extends Copyable implements Thing {
public String content = null;
@Override
public Copyable createCopy() {
Test clone = new Test();
clone.content = this.content;
return clone;
}
}
/*code somewhere*/ {
Test t1 = new Test();
t1.content = "Hello world!";
Test t2 = (Test)t1.copy();
System.out.println(t2.content);
}
Одна из проблем с этим, является что Copyable
не является интерфейсом. Однако это можно использовать без особых усилий, как показано в примере, но используемая проверка класса не поддерживается на уровне языка. Другими словами, абстрактный метод createCopy
не ограничивается классом, который он копирует, и все, что зависит от программиста, который расширяет класс Copyable
или класс, который его расширяет.
Положительная сторона состоит в том, что если вы вызываете объект .copy()
, он должен возвращать объект, такой же, как и сам. Вместо исключения вы можете вернуть null
, если хотите. Тогда вы получили хорошее или ничего.
Но, честно говоря, я не совсем понимаю, почему у вашего локального метода createCopy
есть параметр. Это может быть то статический метод ... altrough я даже не могу себе представить, что бы в этот блок коды:
static <X extends Thing> X copy(X object) { ... }
Может вы могли бы сочетать pratice со статическим универсальным методом и результат становится немного более дружественным :
public interface Thing extends Cloneable {
public static <X extends Thing> X copy(X thing) {
Object clone = thing.clone();
if (clone.getClass() != getClass())
throw new RuntimeException("Copy has wrong type!");
return (X)clone;
}
}
public class ThingA implements Thing {
public Object clone() { ... }
}
/*code somewhere*/ {
ThingA a1 = new ThingA();
ThingA a2 = Thing.copy(a1);
}
Тем не менее, метод клонирования регулируется исключение вместо ограничения языка, но я думаю, что это далеко не лучшее решение.
Это все еще позволяет ему делать именно то, чего он не хочет. Дженерики не мешают вам использовать только один тип класса, поскольку это будет общая противоположность цели дженериков. – giorashc