Recently, while writing test, I encountered enexpected (for myself) Java behaviour. In simplified form, the situation was as in the code below. I expected that the testRunnable
would invoke run(Runnable)
method, but instead run(Supplier)
was invoked.
public void testRunnable() {
run(() -> {
throw new RuntimeException();
});
}
public void run(Supplier<?> supplier) {
supplier.get();
}
public void run(Runnable runnable) {
runnable.run();
}
But it is not a bug, run(Supplier)
was chosen because it has more specific signature. There is method for this - com.sun.tools.javac.comp.Resolve#signatureMoreSpecific
. In simplified form it looks like code below.
boolean signatureMoreSpecific(Method m1, Method m2) {
var methodInstance = instantiate(m2, m1.args)
return methodInstance != null;
}
How this method works will be clearer with more obvious example. Let’s say we have method print(String)
and print(Object)
. Then we try to instantiate print(Object)
with String
argument. Obviously it will be instantiated successfully. Then we try instantiate print(String)
with Object
argument. It will fail, because we can’t pass Object
in method which consume String
.
signatureMoreSpecific("print(String)", "print(Object)")
-> instantiate("print(Object)", "String")
-> success
-> true
signatureMoreSpecific("print(Object)", "print(String)")
-> instantiate("print(String)", "Object")
-> fail
-> false
So method signature is more specific if it can be passed into other method and signature of this method can’t be passed into first method.
Method mostSpecific(Method m1, Method m2) {
boolean m1SignatureMoreSpecific = signatureMoreSpecific(m1, m2);
boolean m2SignatureMoreSpecfic = signatureMoreSpecific(m2, m1);
if (m1SignatureMoreSpecific && m2SignatureMoreSpecific) {
// …
}
else if (m1SignatureMoreSpecific) return m1;
else if (m2SignatureMoreSpecific) return m2;
else throw e;
}
Top comments (0)