- ベストアンサー
実行時に確定するメソッドを呼び出す方法について
invoke()メソッドや、Methodクラスを 使用するなど。以前に小耳にはさんだ。記憶があります。 以下の3つの引数から 動的に該当メソッドを呼び出す。 処理を実装コードがすぐだせる方がいると 助かります。 Object foo(Object obj, String methodName, Collection param) (1)該当オブジェクト getClass()などで、該当クラスを動的に 特定。 (2)、(1)が保有しているメソッドのうち、 発行したいメソッドの名前 (3) parmには1要素ごとに2要素のObject[]型を を格納して、 Object[0]の要素にかんしてはが引数の型についての完全修飾クラス名をあらわすString型。 Object[1]については実際に入っている値。 上記3つの引数をうけとって内部で getClass()とかつかって 必要な情報はすべて実行時に動的に 取得して objの該当オブジェクトを 発行し、foo()メソッド自体の 返却値はそのメソッドの返却値とする という ロジックの実装のサンプルがあると。 とても、うれしいです。 じゃ、void型が返却の時どうすんねん。 とか、staticの時どうすんねん。 とか、その変のアイデア持ってる 人がいるととてもうれしい。 以上
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
こんにちは(^ー^) 即席のソースですので細かいところはさておき、 だいたいのイメージがつかめていただければと思います。 (以下サンプル) --------------------------------------- public Object invoke(Object instance, String methodName, Collection param) throws Exception { Object ret = null; Class paramType[] = null; Object paramValue[] = null; if(param != null && !param.isEmpty()){ int paramNum = param.size(); paramType = new Class[paramNum]; paramValue = new Object[paramNum]; Iterator it = param.iterator(); int i=0; while(it.hasNext()){ Object elem[] = (Object[])it.next(); if(elem[0] != null){ paramType[i] = elem[0].getClass(); }else{ paramType[i] = null; } Object value = new Object(); paramValue[i++] = elem[1]; } } try{ Class classType = instance.getClass(); Method method = classType.getMethod(methodName, paramType); ret = method.invoke(instance, paramValue); }catch(Exception e){ throw e; } return ret; } 1.戻り値がvoidの場合はnullが返ります。 実行エラーの場合もnullが返ります。 その区別のため、上記サンプルでは、エラーの場合、例外を投げるようにしています。 2.staticメソッドの場合も特に意識する必要はないと思います。第1引数のオブジェクトが無視されるだけです。 ご参考になれば幸いです。
その他の回答 (4)
- ngsvx
- ベストアンサー率49% (157/315)
Object foo(Object obj, String methodName, List params) throw XXXXException { Class cls = obj.getClass(); //findMethod用のパラメータを作る String[] paramTypeNames = new String[params.size()]; for(int i = 0 ; i < param.size() ; i++){ Object[] param = (Object[])params.get(i); paramTypeNames[i] = (String)param[0]; } //該当メソッドの取得 Method method = findMethod(cls, methodName, paramTypeNames); if(method == null){ throw new XXXXException(); } //Method#invoke用のパラメータを作成 Object[] values = new Object[param.size()]; for(int i = 0 ; i < param.size() ; i++){ Object[] param = params.get(i); values[i] = param[1]; } Object rtn = null; try{ rtn = method.invoke(obj, values); return rtn; }catch(Excetion e){ e.printStackTrace(); } } /** クラスから条件に合うメソッドを探す */ protected Method findMethod(Class cls, String methodName, String[] paramTypeNames) { Method[] methods = cls.getMethods(); for(int i = 0 ; i < methids.length ; i++){ //違うメソッド名をはじく if( ! methods[i].getName().equals(methodName) ){ continue; } //パラメータの数ではじく Class[] params = methods[i].getParameterTypes(); if(params.length != paramTypeNames.length){ continue; } //パラメータをチェック if( !checkParam(params, paramTypeNames) ){ continue; } //一致したときの処理 return methods[i]; } return null; } /**引数の型が同じかチェックする */ protected boolean checkParam(Class[] params, String[] params){ for(int i = 0 ; i < params.length ; i++){ Object[] param = (Object[])params.get(i); if( !params[i].getName().equals(param[i]) ){ return false; } } return true; } Collectionインターフェースでは、順序が保証されていないため、 Listインターフェースを使うのがいいと思います。 また、プリミティブ型のClassオブジェクトは、Class.forNameでは取得できないので、 Class#getMethods() でMethodの一覧を取得し、その中から対象となるものを選択しています。 例外処理は、適当なものを作ってください。
お礼
プリミティブ型は Collection型の問題点の 指摘まで、 いただいて大変参考になります。 危うく、「なんでじゃー!!」 ってはまってしまうところでした。 本当に助かります。
- nontatta
- ベストアンサー率34% (18/52)
たびたびすみません。 先の私の投稿に誤りがありましたので、訂正します。 ×:【また、先のサンプルでは、戻り値がvoidの場合(恐らくセッターの場合?)は、第3引数のparamにnullが渡されることを前提としています。ClassクラスのgetMethodメソッド呼び出し時や、Methodクラスのinvokeメソッド呼び出し時に、戻り値がvoidの場合は第2引数にnullを渡さなくてはならないので、ソース簡略化のため?そうしています。つまり、先のサンプルメソッドを利用してvoidメソッドを呼び出す場合、第3引数のparamに、Collectionそのものがnullでなく、nullもしくは空の要素をもったCollectionが渡されると例外が発生しています。】 の部分ですが、戻り値がvoidでなく、引数がないメソッド呼び出しの場合についての記述です。 よって、 ○:【また、先のサンプルでは、引数がないメソッドの場合(恐らくゲッターの場合?)は、第3引数のparamにnullが渡されることを前提としています。 ClassクラスのgetMethodメソッド呼び出し時や、Methodクラスのinvokeメソッド呼び出し時に、引数がないメソッドの場合は第2引数にnullを渡さなくてはならないので、ソース簡略化のため?そうしています。つまり、先のサンプルメソッドを利用して引数がないメソッドを呼び出す場合、第3引数のparamに、Collectionそのものがnullでなく、nullもしくは空の要素をもったCollectionが渡されると例外が発生しています。】 が正しい文章です。 すみませんが、下の文章に読み替えてくださいm(__)m
- nontatta
- ベストアンサー率34% (18/52)
こんにちは(^ー^) あぁぁ... >Object[0]の要素にかんしてはが引数の型についての完全修飾クラス名をあらわすString型 でしたね。すみません。 よって、 paramType[i] = elem[0].getClass(); ↓ paramType[i] = Class.forName(elem[0]); です。 ただ、Class.forName("xxxxx")は例外発生時、例外を投げるので、 このステップもtryブロック内に入れなくてはならないでしょう。 また、先のサンプルでは、戻り値がvoidの場合(恐らくセッターの場合?)は、第3引数のparamにnullが渡されることを前提としています。 ClassクラスのgetMethodメソッド呼び出し時や、Methodクラスのinvokeメソッド呼び出し時に、戻り値がvoidの場合は第2引数にnullを渡さなくてはならないので、ソース簡略化のため?そうしています。つまり、先のサンプルメソッドを利用してvoidメソッドを呼び出す場合、第3引数のparamに、Collectionそのものがnullでなく、nullもしくは空の要素をもったCollectionが渡されると例外が発生しています。 すでにお気づきだったかもしれませんが、分かりづらい日本語ですみませんm(__)m
- kacchann
- ベストアンサー率58% (347/594)
import java.lang.reflect.*; class Sample { public static void main(String[] args) { out("Math#pow(double, double)"); Object[] argList = new Object[]{new Double(3), new Double(4)}; Object res = call(null, "java.lang.Math", argList, "pow"); out(res+"\n"); out("StringBuffer#insert(int, char[]);"); char[] charArr = new char[]{'x','y','z'}; argList = new Object[]{new Integer(2), charArr}; res = call(new StringBuffer("aaaa"), null, argList, "insert"); out(res+"\n"); } public static Object call(Object o, String n, Object[] args, String methodName) { Object r = null; Class c = null; if (null == (c=getClass(o,n))) {return null;} Class[] typs = null; if (null != args) { int l = args.length; typs = new Class[l]; for (int i = 0; i < l; i++) { typs[i] = getClass(args[i]); } } try { r = c.getMethod(methodName, typs).invoke(o, args); } catch (Exception e) { e.printStackTrace(); } return r; } private static Class getClass(Object o, String name) { Class c = null; if (null != o) { c= o.getClass(); } else { try { c = Class.forName(name); }catch (ClassNotFoundException e) { e.printStackTrace(System.err); } } return c; } private static Class getClass(Object o) { if (null == o) {return null;} Class prmtv; Class c = o.getClass(); if ( null != (prmtv=getPrmtv(o.getClass().getName()))){ c = prmtv; } return c; } public static Class getPrmtv(String name) { Class r = null; if (name.equals("java.lang.Double")) { r = Double.TYPE; } else if (name.equals("java.lang.Integer")) { r = Integer.TYPE; } return r; } private static void out(String s) { System.out.println(s); } }
お礼
どうも、ありがとう。 JSPに埋め込むカスタムタグクラスを開発する必要があって 月曜日か火曜日あたりに着手するんですけど。 カスタムタグに引数として "スコープにおける名前.フィールド名" を渡すのですが。 "スコープにおける名前" のインスタンスのクラス名を 動的に取得して、 "フィールド名" の頭文字を大文字にして get~() を呼んで、 値を取得したあと。 自分のやりたいロジックを 組みたかったんです。 nontatta さんの回答のおかげで できそうです。 これがないと、 引数でもらうデータの種類だけ 条件分岐したり、 メンテナンスが発生しそうで やりたくなかったんです。 ただ、ひとつ疑問があるのですが。 paramType[i] = elem[0].getClass(); ↑これです。 メソッドの第3引数の Collection param の各要素には Object[2]がつまっており、 Object[0]は完全クラス名のString型です。 "jp.co.hoge.Zoo".getClass() はString型のClassクラスが返却されるのでは? それともZooクラスの Classクラスが返却されるのでしょうか? あれれ?