導入
Javaにおける動的メソッド呼び出しといえば、長らくjava.lang.reflect.Methodが主流でしたが、パフォーマンスと型安全性の面で課題がありました。Java 7で導入されたMethodHandleは、これらを解決し、より高速かつ柔軟なメタプログラミングを可能にします。フレームワーク開発や、動的なプロキシ生成を行う際に必須となるこの技術をマスターしましょう。
基礎知識
MethodHandleは、メソッドやフィールド、コンストラクタへの「型安全な参照」です。Reflectionとの最大の違いは、JVMレベルで最適化が可能である点です。
・invokeExact: 引数の型と戻り値の型が完全に一致している必要があり、最も厳密です。
・invoke: 引数の型を自動変換(ボックス化や拡大変換)して呼び出します。
・invokeWithArguments: 引数を配列で渡すため、可変長引数の扱いに適しています。
・bindTo: インスタンスメソッドを、特定のオブジェクトに紐づけて「呼び出し可能な状態」にします。
実装/解決策
MethodHandleを使用するには、まずMethodHandles.Lookupクラスを介して取得します。対象のクラス、メソッド名、メソッドのシグネチャ(MethodType)を指定するのが定石です。
サンプルプログラム
以下のコードは、文字列の連結処理をMethodHandle経由で動的に実行する例です。
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
public class MethodHandleExample {
public static void main(String[] args) throws Throwable {
// 1. ルックアップの取得
MethodHandles.Lookup lookup = MethodHandles.lookup();
// 2. メソッドのシグネチャ定義 (戻り値: String, 引数: String, String)
MethodType type = MethodType.methodType(String.class, String.class, String.class);
// 3. Stringクラスのconcatメソッドを取得
MethodHandle mh = lookup.findVirtual(String.class, “concat”, type);
// 4. bindToを使用して、”Hello, “に固定する
MethodHandle boundMh = mh.bindTo(“Hello, “);
// 5. invokeで実行
String result = (String) boundMh.invoke(“World!”);
System.out.println(“結果: ” + result);
// 6. invokeWithArgumentsの例(引数を配列で渡す)
Object[] params = {“Java”, “MethodHandle”};
String result2 = (String) mh.invokeWithArguments(params);
System.out.println(“配列指定の結果: ” + result2);
}
}
応用・注意点
現場での注意点は、アクセス制御です。MethodHandles.lookup()はそのクラス内でのみ有効な権限を持つため、外部クラスのprivateメソッドにアクセスする場合は適切なLookupの生成が必要です。
また、invokeExactは型が一つでもずれるとWrongMethodTypeExceptionが発生します。基本的には柔軟性の高いinvokeを推奨しますが、パフォーマンスを極限まで追求するホットスポットでは、適切な型指定を行いinvokeExactを利用することで、JITコンパイラの最適化を最大限に引き出すことが可能です。Reflectionよりも実行速度が格段に速いため、頻繁に呼び出す動的なメソッド処理にはMethodHandleを積極的に活用してください。

コメント