クラスの外からprivateメソッドにアクセスする

どんなものでも抜け道はあるのである。
javaでは通常、privateメソッドは、他のクラスから呼ぶことはできない。しかしリフレクションを使えば、それができてしまう。

以下に、任意のオブジェクトの、privateメソッドを呼びだすサンプルプログラムを示す。

public class Hoge {
  private int sum(int a, int b) {
    return a+b;
  }
}

public class ReflectUtil {

  public static Object forceInvoke(Object obj, String methodName, Object... args)
                                   throws NoSuchMethodException {
    try {
      Class c = obj.getClass();
      Class[] argClasses = null;
      if (args!=null) {
        List<Class> argClassList = new ArrayList<Class>();
        for (Object object : args) {
          argClassList.add(object.getClass());
        }
        argClasses = argClassList.toArray(new Class[0]);
      }

      Method m = null;
      try {
        m = c.getDeclaredMethod(methodName, argClasses);
      }
      catch (NoSuchMethodException e) {
        try {
          m = c.getMethod(methodName, argClasses);
        } catch (NoSuchMethodException e1) {
          throw e1;
        }
      }
      
      boolean isAccessible = m.isAccessible();
      m.setAccessible(true);
      
      Object result =  m.invoke(obj, args);
      
      m.setAccessible(isAccessible);
      
      return result;
      
    } catch (SecurityException e) {
      e.printStackTrace();
    } catch (IllegalArgumentException e) {
      e.printStackTrace();
    } catch (IllegalAccessException e) {
      e.printStackTrace();
    } catch (InvocationTargetException e) {
      e.printStackTrace();
    }
    
    return null;
  }
  
  public static void main(String[] args) throws NoSuchMethodException {
    Hoge hoge = new Hoge();
    System.out.println(forceInvoke(hoge, "sum", 100,200));
  }
}

この例では、Hogeクラスのprivateメソッド、sumを呼び出した。
実行結果は以下の通りであり、privateなメソッドにアクセスできていることが分かる。

300

privateメソッドにアクセスするためのポイントは二つある。
一つは、Class#getDeclaredMethodを用いて、呼び出したいメソッドのMethodオブジェクトを得ることである。ClassオブジェクトからMethodオブジェクトを得る方法としては、もうひとつClass#getMethodがあるが、こちらはpublicメソッドのみしか取得できない。Class#getDeclaredMethodなら、privateメソッドも含めて取得することができる。

もう一つは、java.lang.reflect.Method#setAccessible を用いて、呼び出したいMethodオブジェクトのaccessibleをtrueにすることである。accessibleの値は、publicメソッドであろうと、privateメソッドであろうと、デフォルトでfalseが設定されているようである。accessibleがfalseかつprivateなメソッドオブジェクトをinvokeしようとすると例外になる。ちなみに、コンストラクタのaccessibleを設定しようとしても例外が発生する。

今回はprivateメソッドを無理矢理呼び出す方法を紹介したが、有効な利用方法があるのかどうかはよく分からない。