DEV Community

Efim Smykov
Efim Smykov

Posted on

Why () -> {throw e} is more Supplier than Runnable

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();
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)