Javaでよく使うユーティリティ(その2)

前回に引き続きプロジェクトでよく使うユーティリティについて。
今回は「リフレクション+Runnable」で、簡単に任意のメソッドを別スレッド起動するユーティリティを紹介します。

◆ユーティリティクラス

public class ThreadUtil {

 public static void invoke(final String methodName, final Object target, final Object... args) {
  if (target == null) {
   return;
  }
  new Thread(new Runnable() {
   public void run() {
    try{
     Method method = target.getClass().getDeclaredMethod(methodName,
       createParameterTypes(args));
     method.setAccessible(true);
     method.invoke(target, args);

    } catch(Throwable e){
     e.printStackTrace();
    }
   }
  }).start();
 }

 public static void invokeForStaticMethod(final String methodName,final Class<?> cls,finalObject... args){
  new Thread(new Runnable() {
   public void run() {
    try{
     Method method = cls.getDeclaredMethod(methodName,
       createParameterTypes(args));
     method.setAccessible(true);
     method.invoke(null, args);

    } catch(Throwable e){
     e.printStackTrace();
    }
   }
  }).start();
 }

 private static Class<?>[] createParameterTypes(Object... args) {
  List<Class<?>> types = new ArrayList<Class<?>>(args.length);
  for (Object arg : args) {
   types.add(arg.getClass());
  }
  return types.toArray(new Class<?>[0]);
 }
}

invokeForStaticMethod() はその名の通り、staticメソッド用のものになります。
invoke()/invokeForStaticMethod()ともに、あるメソッドのシグネチャを判定して実行するつくりとなっています。
こんな感じで使います。

◆ユーティリティクラスを使ってみる

public class Main {

 public static void main(String[] args) {
  Main main = new Main();

  // privateメソッドも実行可能
  ThreadUtil.invoke("test_1", main, (Object)args);

  // 複数引数ももちろん可能
  ThreadUtil.invoke("test_2", main, args, 3, "カウント");

  // staticメソッドを実行
  ThreadUtil.invokeForStaticMethod("test_3", Main.class, 3);
 }

 private void test_1(String[] args) {
  System.out.println("test");
 }

 protected void test_2(String[] args, Integer cnt, String aaa) {
  System.out.println(aaa + ": " + cnt);
 }

 static void test_3(Integer cnt){
  System.out.println("static method invoke.");
 }
}

ThreadUtil.invoke()の第3引数を可変引数としているので、test_2()のように複数引数の場合にも単純にパラメータを指定するだけで良いです。
(たとえばObject とかにしていると、いちいちObjectインスタンスを生成しないといけない。)
でもその弊害で、test_1()のように配列のみの引数をとるものについてはObjectでキャストしないといけません。
可変引数は最終的には配列として処理されるため、引数が1つだとどう扱っていいかわからないんですね〜。
私は可変引数が好みですが、Object[] 好みの方はそちらでも良いかと思います。

あと、これの欠点なんですが、実行したいメソッドの引数にプリミティブ型(int、booleanとか)がある場合は使えません。。。
(getDeclaredMethod()では、これらのラッパークラスInteger、Boolean とかで検索しにいくので。)
もしそういったメソッドを実行したい場合は、getDeclaredMethods()とかでメソッド全部取得して、メソッド名のみで判定するとか・・・本質的な解決ではないですが、手段はあると思います。