A DESCRIPTION OF THE PROBLEM :
Lookup.unreflectSpecial throws an Exception even when a call to Lookup.findSpecial with parameters extracted from the Method succeed.
This especially happens, when a default method of an interface should be looked up.
Using Lookup.unreflectSpecial() is useful for InvokationHandlers to invoke default methods.
This difference between unreflectSpecial and findSpecial is at least surprising.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
This works:
Try to lookup a MethodHandle for a default method using Lookup.findSpecial with the interface as special caller.
This fails:
Try to lookup a MethodHandle for a default method using Lookup.unreflectSpecial with the interface as special caller.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Lookup.unreflectSpecial is as least as powerful as Lookup.findSpecial.
ACTUAL -
Lookup.unreflectSpecial throws an IllegalAccessException.
Exception in thread "main" java.lang.IllegalAccessException: no private access for invokespecial: interface java.util.Comparator, from bug.UnreflectSpecial (unnamed module @6c629d6e)
at java.base/java.lang.invoke.MemberName.makeAccessException(MemberName.java:942)
at java.base/java.lang.invoke.MethodHandles$Lookup.checkSpecialCaller(MethodHandles.java:2231)
at java.base/java.lang.invoke.MethodHandles$Lookup.unreflectSpecial(MethodHandles.java:1795)
at bug.UnreflectSpecial.unreflectSpecial(UnreflectSpecial.java:34)
at bug.UnreflectSpecial.main(UnreflectSpecial.java:22)
---------- BEGIN SOURCE ----------
package bug;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.Comparator;
public class UnreflectSpecial {
private static final Lookup LOOKUP = MethodHandles.lookup();
public static void main(String[] args) throws Throwable {
Method method = Comparator.class.getMethod("reversed");
Comparator<Object> cmp = new TestInterf();
MethodHandle mhFind = findSpecial(method);
Object findRev = mhFind.invoke(cmp);
System.out.println(findRev);
MethodHandle mhUnreflect = unreflectSpecial(method);
Object unreflectRev = mhUnreflect.invoke(cmp);
System.out.println(unreflectRev);
}
static MethodHandle findSpecial(Method m) throws ReflectiveOperationException {
return LOOKUP.findSpecial(m.getDeclaringClass(), m.getName(),
MethodType.methodType(m.getReturnType(), m.getParameterTypes()),
m.getDeclaringClass());
}
static MethodHandle unreflectSpecial(Method m) throws ReflectiveOperationException {
return LOOKUP.unreflectSpecial(m, m.getDeclaringClass());
}
static class TestInterf implements Comparator<Object> {
public int compare(Object o1, Object o2) {
return 0;
}
@Override
public Comparator<Object> reversed() {
System.out.println("UnreflectSpecial.TestInterf.reversed()");
return this;
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Use Lookup.findSpecial instead.
FREQUENCY : always