インスタンスを拡張するという意味でMixinできなかな?と思ったので、いくつかアイデア。
たとえば、汎用的なtoString()を追加する。
ターゲットはこんな感じ。
public interface Cat {
String meow();
String run();
}public class CatImpl implements Cat {
public String meow() { return "cat.meow"; }
public String run() { return "cat.run"; }
}
拡張用のモジュールはこんな感じ。
public interface Module {
public void setBody(Object body);
public boolean defined(Method method);
}public class ToStringModule implements Module {
private Object body = null;
public void setBody(Object body) { this.body = body; }
public boolean defined(Method method) {
return "toString".equals(method.getName());
}public String toString() {
return "クラス名: " + body.getClass().getName() + " / ハッシュ値: " + body.hashCode();
}}
で、テスト。
public class MixinTest {public static void main(String args) {
Cat cat = (Cat) Mixin.create(new CatImpl(), new ToStringModule());
System.out.println(cat.meow());
System.out.println(cat.toString());
}}
cat.meow
クラス名: bar.CatImpl / ハッシュ値: 19770577
仕掛けはこんな感じ。
public class Mixin {public static Object create(Object body, Module module) {
InvocationHandler handler = new MixinInvocationHandler(body, module);
return Proxy.newProxyInstance(Mixin.class.getClassLoader(), body.getClass().getInterfaces(), handler);
}}
class MixinInvocationHandler implements InvocationHandler {
private Object body = null;
private Module module = null;
public MixinInvocationHandler(Object body, Module module) {
this.body = body;
this.module = module;
module.setBody(body);
}public Object invoke(Object proxy, Method method, Object args) throws Throwable {
if (module.defined(method))
return method.invoke(module, args);
else
return method.invoke(body, args);
}}
toString()のようなメソッドでない限り、追加するメソッドのインターフェースを新しく作る必要があるのが、ちょっとメンドい。
バイトコードをいじれると、もう少し楽なのかな?
追記
でも、RubyのMixinてクラス定義時にやっているような。
動的な機能の拡張はMixinとは呼ばないかなぁ…うーん…