Having a dynamic proxy for an interface with default methods, how do I invoke a default method? By using something like defaultmethod.invoke(this, ...) you just
This is annoyingly stupid counter-intuitive behaviour, which I assert is a bug in method#invoke(Object,Object[]), because you can't keep things simple in an InvocationHandler, like:
if (method.isDefault())
method.invoke(proxy, args);
else
method.invoke(target, args); // to call a wrapped object
So have to do a special lookup for a MethodHandle, and bind to proxy, to call, it.
I refined the McDowell provided code as follows (simplified):
private static final Constructor lookupConstructor;
static {
try {
lookupConstructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
lookupConstructor.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
private static MethodHandle findDefaultMethodHandle(Class> facadeInterface, Method m) {
try {
Class> declaringClass = m.getDeclaringClass();
// Used mode -1 = TRUST, because Modifier.PRIVATE failed for me in Java 8.
MethodHandles.Lookup lookup = lookupConstructor.newInstance(declaringClass, -1);
try {
return lookup.findSpecial(facadeInterface, m.getName(), MethodType.methodType(m.getReturnType(), m.getParameterTypes()), declaringClass);
} catch (IllegalAccessException e) {
try {
return lookup.unreflectSpecial(m, declaringClass);
} catch (IllegalAccessException x) {
x.addSuppressed(e);
throw x;
}
}
} catch (RuntimeException e) {
throw (RuntimeException) e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static class InvocationHandlerImpl implements InvocationHandler {
private final Class> facadeInterface;
private Object invokeDefault(Object proxy, Method method, Object[] args) throws Throwable {
MethodHandle mh = findDefaultMethodHandle(facadeInterface, m);
return mh.bindTo(proxy).invokeWithArguments(args);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.isDefault()) {
return invokeDefault(proxy, method, args);
}
// rest of code method calls
}
}
facadeInterface is the interface being proxied, which declares the default method, it will probably be possible to use super-interface default methods too.
Non-toy code should do this lookup before invoke is called, or at least cache the MethodHandle.