- ベストアンサー
method#invoke のラッパ
下記のようなリフレクション(動的なメソッド呼び出し)の汎用的なラッパ関数を作成しています。 class Wrapper { public static void main(String[] args) { Hoge hoge = new Hoge(); // (1) Wrapper.call(hoge, "testMethod", new Human()); // (2) これはNoSuchMethodException例外発生 Wrapper.call(hoge, "testMethod", new Japanese()); // (3) これもNoSuchMethodException Wrapper.call(hoge, "testMethod", (Human) new Japanese()); } public static Object call(Object obj, String method_name, Object ...args) { try { // 引数のクラス配列を取得 Class[] class_ary = new Class[args.length]; for (int i=0; i<args.length; i++) class_ary[i] = args[i].getClass(); Class c = obj.getClass(); // メソッドインスタンスを取得 Method method = c.getMethod(method_name, class_ary); // 実行 return method.invoke(obj, args); }catch (Exception e) { e.printStackTrace(); return null; } } } class Hoge { public void testMethod(Human h) { System.out.println(h.getClass()); } } class Human { } class Japanese extends Human { } 一見うまくうごくようにみえたのですが (2)のようにすると例外が発生することが最近わかりました。 親クラスにキャストした(3)でも同様です。 理由は // 引数のクラス配列を取得 Class[] class_ary = new Class[args.length]; for (int i=0; i<args.length; i++) class_ary[i] = args[i].getClass(); のgetClass()の部分がキャストしようがしまいが Japaneseのクラスインスタンスを返すからだというのはわかっているのですが、他の方法が思いつきません。 多態性を備えたラッパ関数を作成することは無理でしょうか? 何か解決策があればアドバイスをお願いします。
- みんなの回答 (1)
- 専門家の回答
質問者が選んだベストアンサー
こんな感じでしょうか。あんまり詳しくチェックしてませんけれど。 public static Object call(Object obj, String method_name, Object... args) { try { // メソッドインスタンスを取得 Method method = findCompatibleMethod(obj.getClass(), args); // 実行 return method.invoke(obj, args); } catch (Exception e) { e.printStackTrace(); return null; } } // argsを引数に呼び出せるメソッドをclazzから探します static Method findCompatibleMethod(Class clazz, Object[] args) throws NoSuchMethodException { for (Method method : clazz.getMethods()) { if (isCompatible(method, args)) { return method; } } throw new NoSuchMethodException(); } static boolean isCompatible(Method method, Object[] args) { Class[] clazzes = method.getParameterTypes(); for (int i = 0; i < args.length; i++) { if (!clazzes[i].isInstance(args[i])) { return false; } } return true; }