I have two classes A and B, both define method foo() with common signature (accept nothing, return void). They don't have the common base class (or interface) that declares this method. I want to call this method on regardless As or Bs as long as they can respond to this call. This approach is called Duck Typing.
I know that there's an instruction called invokedynamic:
Each instance of an invokedynamic instruction is called a dynamic call site. A dynamic call site is originally in an unlinked state, which means that there is no method specified for the call site to invoke. As previously mentioned, a dynamic call site is linked to a method by means of a bootstrap method. A dynamic call site's bootstrap method is a method specified by the compiler for the dynamically-typed language that is called once by the JVM to link the site. The object returned from the bootstrap method permanently determines the call site's behavior.
So I tried to achive this using MethodHandles. Here's the example:
public static class A {
public void foo() {
}
}
public static class B {
public void foo() {
}
}
public static void main(String[] args) throws Throwable {
final MethodHandle foo = MethodHandles.lookup()
.findVirtual(A.class, "foo", MethodType.methodType(void.class));
foo.invoke(new B());
}
Of course, I've got:
Exception in thread "main" java.lang.ClassCastException: Cannot cast Main$B to Main$A
at sun.invoke.util.ValueConversions.newClassCastException(ValueConversions.java:461)
at sun.invoke.util.ValueConversions.castReference(ValueConversions.java:456)
at Main.main(Main.java:30)
I clearly see the difference between invokedynamic and MethodHanle. I see that the problem is that the foo MethodHandle is bound to the class A, not class B. But is it possible for me to somehow take advantage of invokedynamic in this particular case?
Why do I need this? This is the part of my small research project. I'm trying to understand method handles in depth and I want to call common methods on annotation instances retrieved from fields and methods. I am unable to define base class for annotations in Java, so instead of chain of instanceof's and class casts or retrieving these values using reflection violating access rights, I want to implement this duck typing if possible.
Thanks.
When the VM encounters an
invokedynamicinstruction for the first time it calls a factory method, or 'bootstrap' method, that returns aCallSiteobject who's target implements the actual functionality. You can implement that yourself using aMutableCallSitethat looks up your target method on first invocation and then sets it's own target to the looked up method.But, this is not enough for your purposes. You want to re-link the call site when you encounter a new receiver type.
Here is an example (that currently only supports
findVirtual):Before the first invocation, calling the call site's dynamic invoker will jump right into the
linkmethod, which will lookup the target method and then invoke that, as well as re-linking the DuckTypingCallSite to basically cache the looked up MethodHandle, guarded by a type check.After the first invocation, this essentially creates an if/else like this:
Then when the second type is encountered it changes to this:
etc.
And here is an example usage: