Proxy を使ってオブジェクトをコピー可能にする
昨日のネタに関して少し思いついたことを。
配列には使えないけど、インターフェースを宣言している場合には java.lang.reflect.Proxy を使えば Dynamic Proxy を生成できる。そうすると動的に copy メソッドを追加される。
public interface Copyable<E> { E copy(); }
としておいて。
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class CopyUtils { @SuppressWarnings("unchecked") public static <E> E toCopyable(E obj) { if (obj instanceof Copyable) { return obj; } InvocationHandler handler = new CopyProxy(obj); ClassLoader loader = CopyUtils.class.getClassLoader(); Class[] origInterfaces = obj.getClass().getInterfaces(); Class[] interfaces = new Class[origInterfaces.length + 1]; interfaces[0] = Copyable.class; System.arraycopy(origInterfaces, 0, interfaces, 1, origInterfaces.length); return (E) Proxy.newProxyInstance(loader, interfaces, handler); } private static class CopyProxy implements InvocationHandler { private static final Method clone; private static final Method copy; private Object obj; static { try { clone = Object.class.getDeclaredMethod("clone"); copy = Copyable.class.getDeclaredMethod("copy"); } catch (NoSuchMethodException e) { throw new InternalError(); } } public CopyProxy(Object obj) { this.obj = obj; } public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { if (m.equals(copy)) { return toCopyable(clone.invoke(obj)); } return m.invoke(obj, args); } } }
使用例
public interface Person { public String getName(); public void setName(String name); }
public class PersonImpl implements Cloneable { private String name; public PersonImpl(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Person: " + name; } @Override public Object clone() { return new PersonImpl(name); } }
public class CopyTest { public static void main(String[] args) { Person a = new PersonImp("Bill Gates"); Person b = CopyUtils.toCopyable(a); Person c = ((Copyable<Person>) c).copy(); } }