リフレクションの罠
今日某MLに,次のコードで例外が投げられるというなかなか面白い問題が投稿されていたので調べてみた.
BeanMap map = Beans.createAndCopy(BeanMap.class, status).execute();
これを簡略化すると,publicなインタフェースInterfaceAを実装したデフォルトアクセスの(publicではない)実装クラスClassBがpublicなメソッドmethodCを持つ場合に,ファクトリメソッドで得たインスタンスinterfaceA(実際に返されるのはClassBのインスタンス)のmethodCをリフレクションを使って呼び出そうとすると,IllegalAccessExceptionが投げられるというものである.
Method method = interfaceA.getClass().getMethod("methodC", null); method.invoke(interfaceA, null);
質問者は実装クラスをpublicにするか,ラッパクラスを用意しろと要求していた.しかし,「それは違うんじゃない?」と思い調べてみると,次のバグレポートが見つかった.
callinreflection gives IllegalAccessException, whereas direct call doesn't
実は正しいコードは以下の通り.つまり,interface.getClass()して返されるのはInterfaceAではなくClassBであり,これではパッケージの外から呼び出すことはできないのだ.つまり,投稿されたライブラリではなく?easar2のバグだったのだ.
Method method = InterfaceA.class.getMethod("methodC", null); method.invoke(interfaceA, null);
さて,これから得られる教訓は以下の通り.