Java 8 introduced default methods to interfaces. Existing InvocationHandler
implementations will not invoke default interface methods. This short article documents the necessary changes. Note: It first describes the implementation based on a reading of the documents and then provides a working implementation for Java 8.
Given the InvocationHandler
implementation:
@Override
public Object invoke(Object proxy,
Method method, Object[] argv) throws Throwable {
Object result = null;
/* * Logic to calculate result. */
return result;
}
Enter fullscreen mode Exit fullscreen mode
the Java 8 solution appears to be to extend the implementation to invoke any interface default methods through a MethodHandle
:
@Override
public Object invoke(Object proxy,
Method method, Object[] argv) throws Throwable {
Object result = null;
Class<?> declaringClass = method.getDeclaringClass();
if (method.isDefault()) {
result =
MethodHandles.lookup()
.in(declaringClass)
.unreflectSpecial(method, declaringClass)
.bindTo(proxy)
.invokeWithArguments(argv);
} else {
/* * Logic to calculate result. */
}
return result;
}
Enter fullscreen mode Exit fullscreen mode
If the Method
is “default” then the target interface method is invoked. Otherwise, the InvocationHandler
implementation processes as before. Any interface default method should be invoked by:
- Finding the
MethodHandles.Lookup
throughMethodHandles.lookup().in(declaringClass)
, - Get a
MethodHandle
bypassing any overriding methods through.unreflectSpecial(method, declaringClass)
, and, - Invoke the method on the
proxy
with.bindTo(proxy).invokeWithArguments(argv)
Unfortunately, this does not work if the declaringClass
is not “private-accessible” to the caller (which is most of the time) resulting in:
Caused by: java.lang.IllegalAccessException: no private access for invokespecial: interface package1.SomeInterface, from package1.SomeInterface/public
at java.lang.invoke.MemberName.makeAccessException(MemberName.java:850)
at java.lang.invoke.MethodHandles$Lookup.checkSpecialCaller(MethodHandles.java:1572)
at java.lang.invoke.MethodHandles$Lookup.unreflectSpecial(MethodHandles.java:1231)
at package2.InvocationHandlerImpl.invoke(InvocationHandlerImpl.java:59)
Enter fullscreen mode Exit fullscreen mode
The actual Java 8 solution is:
@Override
public Object invoke(Object proxy,
Method method, Object[] argv) throws Throwable {
Object result = null;
Class<?> declaringClass = method.getDeclaringClass();
if (method.isDefault()) {
Constructor<MethodHandles.Lookup> constructor =
MethodHandles.Lookup.class.getDeclaredConstructor(Class.class);
constructor.setAccessible(true);
result =
constructor.newInstance(declaringClass)
.in(declaringClass)
.unreflectSpecial(method, declaringClass)
.bindTo(proxy)
.invokeWithArguments(argv);
} else {
/* * Logic to calculate result. */
}
return result;
}
Enter fullscreen mode Exit fullscreen mode
This will not work in Java 9+. In Java 9 and subsequent releases, the solution should be based on MethodHandles.Lookup.findSpecial()
and/or MethodHandles.privateLookupIn()
.
原文链接:Adding Support to Java InvocationHandler Implementations for Interface Default Methods
暂无评论内容