Why Doesn't Java 8 Type Inference Consider Exceptions Thrown by Lambdas in Overload Selection? Why Doesn't Java 8 Type Inference Consider Exceptions Thrown by Lambdas in Overload Selection? java java

Why Doesn't Java 8 Type Inference Consider Exceptions Thrown by Lambdas in Overload Selection?


If it makes you feel any better, this topic was indeed carefully considered during the JSR-335 design process.

The question is not "why isn't it able", but "why did we choose not to." When we find multiple potentially applicable overloads, we certainly could have chosen to speculatively attribute the lambda body under each set of signatures, and prune those candidates for which the lambda body failed to type-check.

However, we concluded that doing so would likely do more harm than good; it means, for example, that small changes to the method body, under this rule, could cause some method overload selection decisions to silently change without the user intending to do so. In the end, we concluded that using the presence of errors in the method body to discard a potentially applicable candidate would cause more confusion than benefit, especially given that there is a simple and safe workaround -- provide a target-type. We felt that reliability and predictability here outweighed optimal concision.


First of all, you don' have to overload :D - overloading is never a necessity; use 2 different method names, e.g. foo and fooX

Secondly, I don't see why you need 2 methods here. If you want to handle checked and unchecked exceptions differently, it can be done at runtime. To achieve "exception transparency", you can do

interface SupplierX<T, X extends Throwable>{    T get() throws X;}<T, X extends Throwable> void foo(Supplier<T, X> supplier)  throws X { .. }foo( ()->"" );  // throws RuntimeExceptionfoo( ()->{ throw new IOException(); } );  // X=IOException

Finally, disambiguity can be achieved throw lambda return type; the compiler uses the return type as if using an argument type for choosing the most specific method. This gives us the idea to wrap the value together with the exception type, as Result<T,X>, a "monad" as they say.

interface Result<T, X extends Throwable>{    T get() throws X;}// error type embedded in return type, not in `throws` clausestatic Result<String,        Exception> m1(){ return ()->{ throw new Exception();};  }static Result<String, RuntimeException> m2(){ return ()->{ return "str";         };  }  // better to have some factory method, e.g. return Result.success("str");public static void main(String[] args){    foo(()->m1());  // foo#2 is not applicable    foo(()->m2());  // both applicable; foo#2 is more specific}interface S1<T> { T get(); }  static <T> void foo(S1<Result<T, ? extends        Exception>> s){    System.out.println("s1");}}interface S2<T> { T get(); }  // can't have two foo(S1) due to erasurestatic <T> void foo(S2<Result<T, ? extends RuntimeException>> s){    System.out.println("s2");}


Any lambda which could be accepted as a Supplier<T> can also be accepted as a ThrowingSupplier<T>. The following compiles:

public static interface ThrowingSupplier<T>{    public T get() throws Exception;}public static <T> void foo(ThrowingSupplier<T> supplier) {}public static String getAString(){    return "Hello";}public static String getAnotherString() throws Exception{    return "World";}public static void main(String[] args) {    foo(()->getAString());    foo(()->getAnotherString());} 

Given the above, you probably don't need this, but if foo must accept a non-throwing Supplier<T>, you can always wrap the Exception-throwing method in a method which launders it into an unchecked Exception:

public static <T> void foo(Supplier<T> supplier) {}public static String getAString(){    return "Hello";}public static String getAnotherString() throws Exception{    return "World";}public static String getAnotherStringUnchecked(){    try{        return getAnotherString();    } catch(Exception e){        throw new RuntimeException("Error getting another string",e);    }}   public static void main(String[] args) throws Exception{    foo(()->getAString());    foo(()->getAnotherStringUnchecked());}