MethodHandleを使用できます。その Javadoc は次のように書いています。
Lookup API でファクトリ メソッドを使用すると、Core Reflection API オブジェクトによって表される任意のクラス メンバーを、動作的に同等のメソッド ハンドルに変換できます。たとえば、リフレクション メソッドは、Lookup.unreflect を使用してメソッド ハンドルに変換できます。結果として得られるメソッド ハンドルは、通常、基になるクラス メンバーへのより直接的かつ効率的なアクセスを提供します。
これによりオーバーヘッドが削減されますが、呼び出しが通常の (非リフレクション) バイト コード命令で行われた場合、メソッド ハンドルは、JVM が採用できる特定の最適化 (メソッドのインライン化など) を妨げます。このような最適化が有益かどうかは、メソッドの使用方法によって異なります (そのコード パスが常に同じメソッドを呼び出す場合は、インライン化が役立ちますが、毎回異なるメソッドである場合は、おそらくそうではありません)。
次のマイクロベンチマークは、リフレクション、メソッド ハンドル、および直接呼び出しの相対的なパフォーマンスに関する大まかなアイデアを提供する場合があります。
package tools.bench;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import java.math.BigDecimal;
public abstract class Bench {
final String name;
public Bench(String name) {
this.name = name;
}
abstract int run(int iterations) throws Throwable;
private BigDecimal time() {
try {
int nextI = 1;
int i;
long duration;
do {
i = nextI;
long start = System.nanoTime();
run(i);
duration = System.nanoTime() - start;
nextI = (i << 1) | 1;
} while (duration < 100000000 && nextI > 0);
return new BigDecimal((duration) * 1000 / i).movePointLeft(3);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
@Override
public String toString() {
return name + "\t" + time() + " ns";
}
static class C {
public Integer foo() {
return 1;
}
}
static final MethodHandle sfmh;
static {
try {
Method m = C.class.getMethod("foo");
sfmh = MethodHandles.lookup().unreflect(m);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) throws Exception {
final C invocationTarget = new C();
final Method m = C.class.getMethod("foo");
final Method am = C.class.getMethod("foo");
am.setAccessible(true);
final MethodHandle mh = sfmh;
Bench[] marks = {
new Bench("reflective invocation (without setAccessible)") {
@Override int run(int iterations) throws Throwable {
int x = 0;
for (int i = 0; i < iterations; i++) {
x += (Integer) m.invoke(invocationTarget);
}
return x;
}
},
new Bench("reflective invocation (with setAccessible)") {
@Override int run(int iterations) throws Throwable {
int x = 0;
for (int i = 0; i < iterations; i++) {
x += (Integer) am.invoke(invocationTarget);
}
return x;
}
},
new Bench("methodhandle invocation") {
@Override int run(int iterations) throws Throwable {
int x = 0;
for (int i = 0; i < iterations; i++) {
x += (Integer) mh.invokeExact(invocationTarget);
}
return x;
}
},
new Bench("static final methodhandle invocation") {
@Override int run(int iterations) throws Throwable {
int x = 0;
for (int i = 0; i < iterations; i++) {
x += (Integer) sfmh.invokeExact(invocationTarget);
}
return x;
}
},
new Bench("direct invocation") {
@Override int run(int iterations) throws Throwable {
int x = 0;
for (int i = 0; i < iterations; i++) {
x += invocationTarget.foo();
}
return x;
}
},
};
for (Bench bm : marks) {
System.out.println(bm);
}
}
}
私のやや時代遅れのノートに
java version "1.7.0_02"
Java(TM) SE Runtime Environment (build 1.7.0_02-b13)
Java HotSpot(TM) Client VM (build 22.0-b10, mixed mode, sharing)
これは次のように表示されます:
reflective invocation (without setAccessible) 568.506 ns
reflective invocation (with setAccessible) 42.377 ns
methodhandle invocation 27.461 ns
static final methodhandle invocation 9.402 ns
direct invocation 9.363 ns
更新: Irreputable が指摘しているように、サーバー VM のパフォーマンス特性は多少異なるため、サーバー VM で MethodHandle を使用することは、それを static final フィールドに配置できる場合にのみ役立ちます。この場合、VM は呼び出しをインライン化できます。
reflective invocation (without setAccessible) 9.736 ns
reflective invocation (with setAccessible) 7.113 ns
methodhandle invocation 26.319 ns
static final methodhandle invocation 0.045 ns
direct invocation 0.044 ns
特定のユースケースを測定することをお勧めします。