1概要

この短い記事では、Java Reflection APIを使用して実行時にメソッドを呼び出す方法について簡単に説明します。


2準備をする

以下の例で使用する簡単なクラスを作成しましょう。

public class Operations {
    public double publicSum(int a, double b) {
        return a + b;
    }

    public static double publicStaticMultiply(float a, long b) {
        return a **  b;
    }

    private boolean privateAnd(boolean a, boolean b) {
        return a && b;
    }

    protected int protectedMax(int a, int b) {
        return a > b ? a : b;
    }
}


3メソッドオブジェクトの取得

まず、呼び出したいメソッドを反映した

Method

オブジェクトを取得する必要があります。メソッドが定義されている型を表す

Class

オブジェクトは、これを行う2つの方法を提供します。


3.1.

getMethod()



getMethod()

を使用して、パブリックメソッドを見つけることができます。静的メソッドでも、クラスまたはそのスーパークラスで定義されているインスタンスでも可能です。

最初の引数としてメソッド名を受け取り、その後にメソッドの引数の型が続きます。

Method sumInstanceMethod
  = Operations.class.getMethod("publicSum", int.class, double.class);

Method multiplyStaticMethod
  = Operations.class.getMethod(
    "publicStaticMultiply", float.class, long.class);


3.2.

getDeclaredMethod()



getDeclaredMethod()

を使用して、クラスで定義されている任意のメソッドを取得できます。

これには、パブリック、保護、デフォルトアクセス、さらにはプライベートメソッドも含まれますが、継承されたものは除外されます。

それは

getMethod()

と同じパラメータを受け取ります。

Method andPrivateMethod
  = Operations.class.getDeclaredMethod(
    "privateAnd", boolean.class, boolean.class);

Method maxProtectedMethod
  = Operations.class.getDeclaredMethod("protectedMax", int.class, int.class);


4メソッドを呼び出す


Method

インスタンスを配置したら、

invoke()

を呼び出して基になるメソッドを実行し、返されたオブジェクトを取得できます。

** 4.1. インスタンスメソッド

インスタンスメソッドを呼び出すには、

invoke()

の最初の引数は、呼び出されるメソッドを表す

Method

のインスタンスである必要があります。

@Test
public void givenObject__whenInvokePublicMethod__thenCorrect() {
    Method sumInstanceMethod
      = Operations.class.getMethod("publicSum", int.class, double.class);

    Operations operationsInstance = new Operations();
    Double result
      = (Double) sumInstanceMethod.invoke(operationsInstance, 1, 3);

    assertThat(result, equalTo(4.0));
}

** 4.2. 静的メソッド

これらのメソッドはインスタンスを呼び出す必要がないので、最初の引数として

null

を渡すことができます。

@Test
public void givenObject__whenInvokeStaticMethod__thenCorrect() {
    Method multiplyStaticMethod
      = Operations.class.getDeclaredMethod(
        "publicStaticMultiply", float.class, long.class);

    Double result
      = (Double) multiplyStaticMethod.invoke(null, 3.5f, 2);

    assertThat(result, equalTo(7.0));
}


5メソッドのアクセシビリティ

  • デフォルトでは、反映されたすべてのメソッドが

    accessible

    であるとは限りません** これは、JVMがそれらを呼び出すときにアクセス制御チェックを実施することを意味します。

たとえば、定義しているクラスの外側のプライベートメソッド、またはサブクラスまたはそのクラスのパッケージの外側からprotectedメソッドを呼び出そうとすると、

IllegalAccessException

が発生します。

@Test(expected = IllegalAccessException.class)
public void givenObject__whenInvokePrivateMethod__thenFail() {
    Method andPrivateMethod
      = Operations.class.getDeclaredMethod(
        "privateAnd", boolean.class, boolean.class);

    Operations operationsInstance = new Operations();
    Boolean result
      = (Boolean) andPrivateMethod.invoke(operationsInstance, true, false);

    assertFalse(result);
}

@Test(expected = IllegalAccessException.class)
public void givenObject__whenInvokeProtectedMethod__thenFail() {
    Method maxProtectedMethod
      = Operations.class.getDeclaredMethod(
        "protectedMax", int.class, int.class);

    Operations operationsInstance = new Operations();
    Integer result
      = (Integer) maxProtectedMethod.invoke(operationsInstance, 2, 4);

    assertThat(result, equalTo(4));
}

  • 反映されたメソッドオブジェクトに対して

    setAccesible(true)

    を呼び出すことで、JVMはアクセス制御チェックを抑制し** 、例外をスローせずにメソッドを呼び出すことができます。

@Test
public void givenObject__whenInvokePrivateMethod__thenCorrect() {
   //...
    andPrivateMethod.setAccessible(true);
   //...
    Boolean result
      = (Boolean) andPrivateMethod.invoke(operationsInstance, true, false);

    assertFalse(result);
}

@Test
public void givenObject__whenInvokeProtectedMethod__thenCorrect() {
   //...
    maxProtectedMethod.setAccessible(true);
   //...
    Integer result
      = (Integer) maxProtectedMethod.invoke(operationsInstance, 2, 4);

    assertThat(result, equalTo(4));
}


6. 結論

この簡単な記事では、リフレクションを通じて実行時にクラスのインスタンスメソッドと静的メソッドを呼び出す方法を説明しました。また、プライベートメソッドと保護されたメソッドを呼び出すときに、反映されたメソッドオブジェクトのアクセス可能フラグを変更してJavaアクセス制御チェックを抑制する方法も示しました。

いつものように、サンプルコードはhttps://github.com/eugenp/tutorials/tree/master/core-java-lang[over on Github]にあります。