java.io.Serializableインタフェースを実装していないビーンに対して、動的プロキシを使ってディープコピーを試みる。→無茶でした。すみません。
まずは参考サイト。小野和俊氏のブログより。
Java Programming Tips:シリアライズを利用したディープコピー
import java.io.*;
public class CopyUtil {
public static Object deepCopy(Serializable obj)
throws IOException, ClassNotFoundException {
if (obj == null) return null;
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(byteOut);
out.writeObject(obj);
ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
ObjectInputStream in = new ObjectInputStream(byteIn);
return in.readObject();
}
}
以下のメソッドも同じようなことをしてますね。
SerializationUtils#clone(java.io.Serializable)
っていうか、ソースを見たら殆ど一緒でした。
ただし今回は Serializable を実装していないオブジェクトのディープコピーが欲しいので、少し試行錯誤。
で、思いついたのがオブジェクトをプロキシで被せる方法。
(・・・が、直列化を利用したディープコピーが何なのか分かってる人にとっては、これが無駄な作業だったことは明白で、自分の無知ぶりを曝け出してしまって何とも恥ずかしいが、こうなったらとりあえずそういうのも含めて残しておくことにした。)
まず、リフレクション・ハンドラを以下の様な感じで作成。
- ディープ・コピーしたいオブジェクト(ビーン)の Getter、Setter を用意。
- readResolve メソッドを定義し、その戻り値として自分自身を返すように実装。
/** リフレクション・ハンドラ */
public class BeanInvocationHandlerimplements InvocationHandler, Serializable {
/** ビーン */
private T bean;
/** デフォルトコンストラクタ */
public BeanInvocationHandler() {
}
/** ビーンのGetter */
public T getBean() {
return this.bean;
}
/** ビーンのSetter */
public setBean(T bean) {
this.bean = bean;
}
/** 直列化したオブジェクトを復元する直前に呼び出されるメソッド */
public Object readResolve() throws ObjectStreamException {
return this;
}
}
それからプロキシ・ファクトリも作成。
/** プロキシ・ファクトリ */
public abstract class ProxyFactory {
/** プロキシを生成するメソッド */
public static <P> P createProxy(Class<P> proxyClass, InvocationHandler invocationHandler) {
ClassLoader classLoader = proxyClass.getClassLoader();
Class[ ] proxyClasses = new Class[ ] {proxyClass};
Object proxy = Proxy.newProxyInstance(classLoader, proxyClasses, invocationHandler);
return proxyClass.cast(proxy);
}
}
で、リフレクション・ハンドラにビーンを渡してから、このハンドラをプロキシ・ファクトリに渡して、プロキシ・オブジェクトを生成。
そしてこのプロキシ・オブジェクトを渡したところ、、、ビーンが直列化できません的なエラーが発生。。。
つまりプロキシで被せた意味なし。
上述の deepCopy メソッドを使ったディープ・コピーの場合、メンバーに一つでも直列化できない(つまり Serializable インタフェースを実装していない)クラスのインスタンスがあると、例外が発生してしまうのだ。*1
自分の無知にちょっと凹んだ。。。
けれども仕方ないので次の方法。
*1:ただし、メンバーのアクセス修飾子に transient を指定しておけば、例外は発生しないものと思われる。