So i have been doing some things that involve Java MethodHandlers
. And the reflection api has been around for a long time so there is a lot of documentation about it.
MethodHandlers
on the other hand hasn't been around as long so there is a lot fewer examples. What im trying to do is to invoke a static void function that mutates a state using the MethodHandlers
Api.
Here is a runnable example that demonstrates my problem:
class Main {
public static void main(String[] args) throws Throwable {
System.out.println("Times before: " + Foobar.getTimes());
final Class<?> aClass = MethodHandles.lookup().findClass(Foobar.class.getName());
incReflectionApi(aClass);
incMethodHandler(aClass);
}
private static void incReflectionApi(Class<?> aClass) throws Throwable {
final Method init = aClass.getMethod("increment", (Class<?>[]) null);
init.invoke(null, (Object[]) null);
System.out.println("Reflection api: " + Foobar.getTimes());
}
private static void incMethodHandler(Class<?> aClass) throws Throwable {
// Here is where we throw
final MethodHandle handle = MethodHandles.lookup().findStatic(aClass, "increment", MethodType.methodType(Void.class));
handle.invoke();
System.out.println("MethodHandler api: " + Foobar.getTimes());
}
public static class Foobar {
private static int times;
public static void increment() {
times++;
}
public static int getTimes() {
return times;
}
}
}
So basically, access the increment function and invoke it. Doing so with reflective api getMethod
works fine. But when i try to use MethodHandler.lookup#findStatic
i get a NoSuchMethodException
.
Here is the output of the example:
Times before: 0
Reflection api: 1
Exception in thread "main" java.lang.NoSuchMethodException: no such method: Main$Foobar.increment()Void/invokeStatic
at java.base/java.lang.invoke.MemberName.makeAccessException(MemberName.java:963)
at java.base/java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:1101)
at java.base/java.lang.invoke.MethodHandles$Lookup.resolveOrFail(MethodHandles.java:2030)
at java.base/java.lang.invoke.MethodHandles$Lookup.findStatic(MethodHandles.java:1102)
at Main.incMethodHandler(scratch_5.java:26)
at Main.main(scratch_5.java:14)
Caused by: java.lang.NoSuchMethodError: 'java.lang.Void Main$Foobar.increment()'
at java.base/java.lang.invoke.MethodHandleNatives.resolve(Native Method)
at java.base/java.lang.invoke.MemberName$Factory.resolve(MemberName.java:1070)
at java.base/java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:1098)
... 4 more
Am i missing something obvious?
You specified Void.class
for the expected return type which represents the wrapper type Void
rather than the primitive void
.
Here’s a tweaked example:
class Main {
public static void main(String[] args) throws Throwable {
System.out.println("Times before: " + Foobar.getTimes());
final Class<?> aClass = Foobar.class;
incReflectionApi(aClass);
incUnreflect(aClass);
incMethodHandle(aClass);
}
private static void incReflectionApi(Class<?> aClass) throws Throwable {
final Method init = aClass.getMethod("increment");
init.invoke(null);
System.out.println("Reflection api: " + Foobar.getTimes());
}
private static void incUnreflect(Class<?> aClass) throws Throwable {
final Method init = aClass.getMethod("increment");
final MethodHandle handle = MethodHandles.lookup().unreflect(init);
handle.invokeExact();
System.out.println("Reflection to MH: " + Foobar.getTimes());
}
private static void incMethodHandle(Class<?> aClass) throws Throwable {
final MethodHandle handle = MethodHandles.lookup()
.findStatic(aClass, "increment", MethodType.methodType(void.class));
handle.invokeExact();
System.out.println("MethodHandle api: " + Foobar.getTimes());
}
public static class Foobar {
private static int times;
public static void increment() {
times++;
}
public static int getTimes() {
return times;
}
}
}