Hi everyone, I recently reported JDK-8209078 "Unable to call default method from interface in another module from named module" [1].
It is possible to call any default interface method directly using MethodHandles.lookup().findSpecial(Interf.class, "defaultMethodName", ..., Intef.class).invoke(...); This only works if the caller is in an unnamed module or Interf is in the same module as the caller. I now want to share some thoughts about this feature, and an idea how it could be fixed, but this probably requires further discussion. 1. Use Case: The most obvious use case is to to "implement" default methods in instances created by j.l.r.Proxy. An implementation of j.l.r.InvocationHandler that handles default methods could look like this: public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.isDefault()) { MethodType mt = methodType(method.getReturnType(), method.getParameterTypes()); Class<?> declaringClass = method.getDeclaringClass(); Lookup l = MethodHandles.lookup(); MethodHandle mh = l.findSpecial(declaringClass, method.getName(), mt, declaringClass) .asFixedArity() .bindTo(proxy); return mh.invokeWithArguments(args); } // Handle the other methods return null; } Such a feature is very valuable so the InvocationHandler does not break if an interface is evolved by adding new default methods. 2. Documentation: When I faced the problem above, I did a search on how to invoke default methods on a proxy. The above code was inspired by this mail [2] from John Rose, where he mentions JDK-8130227 "JEP 274: Enhanced Method Handles" [3][4]. This is the only official documentation I could find on this feature. Indeed, even the Javadoc for findSpecial [5] states that my code above should not successfully run: > Before method resolution, if the explicitly specified caller class is > not identical with the lookup class, or if this lookup object does not > have private access privileges, the access fails. The specified caller class in the example above is the interface itself, not the lookup class. The documentation has to be at least changed to reflect this feature. 3. Cause of the discrepancy between lookup class in named module vs unnamed module As Mandy Chung observed: > IAE is thrown because the implementation calls Lookup::in teleporting > to a Lookup of the special class which lost all access since the > caller class and the requested lookup class are in different modules. This behavior is documented in the Lookup.in() [6]: > * If the old lookup class is in a named module, and the new lookup > class is in a different module M, then no members, not even public > members in M's exported packages, will be accessible. The exception to > this is when this lookup is publicLookup, in which case PUBLIC access > is not lost. > * If the old lookup class is in an unnamed module, and the new lookup > class is a different module then MODULE access is lost. This strikes me as a little bit odd, so I looked around for the reasoning for this difference, and found an old discussion on jigsaw-dev [7]. I especially found this mail from Alan Bateman [8] interesting, where he gave a reasoning for this: > Preserved but perhaps with the (initially surprising) consequence that > all access is lost when m(LC) is a named module and m(A) is a > different module. This arises because the two modules may read very > different sets of modules, the intersection cannot be expressed via a > lookup class + modes. While I agree that intersection might not be expressed via lookup class + modes, I don't think that it is necessary to express that. Instead don't allow any lookup on members in a different module if the MODULE flag is not set. But this is only a suggestion. It's up to you to decide how to deal with this. Please keep up the good work you are doing, and I hope this mail will help you fixing this bug. Thanks for your time, Johannes Kuhn [1]: https://bugs.openjdk.java.net/browse/JDK-8209078 [2]: http://mail.openjdk.java.net/pipermail/jigsaw-dev/2017-January/010741.html [3]: https://bugs.openjdk.java.net/browse/JDK-8130227 [4]: http://openjdk.java.net/jeps/274 [5]: https://docs.oracle.com/javase/10/docs/api/java/lang/invoke/MethodHandles.Lookup.html#findSpecial(java.lang.Class,java.lang.String,java.lang.invoke.MethodType,java.lang.Class) [6]: https://docs.oracle.com/javase/10/docs/api/java/lang/invoke/MethodHandles.Lookup.html#in(java.lang.Class) [7]: http://mail.openjdk.java.net/pipermail/jigsaw-dev/2015-December/005751.html [8]: http://mail.openjdk.java.net/pipermail/jigsaw-dev/2015-December/005768.html