40

I have a question regarding Java 8 inference with respect to lambdas and their related exception signatures.

If I define some method foo:

public static <T> void foo(Supplier<T> supplier) {
    //some logic
    ...
}

then I get the nice and concise semantic of being able to write foo(() -> getTheT()); in most cases for a given T. However, in this example, if my getTheT operation declares that it throws Exception, my foo method which takes a Supplier no longer compiles: the Supplier method signature for get doesn't throw exceptions.

It seems like a decent way to get around this would be to overload foo to accept either option, with the overloaded definition being:

public static <T> void foo(ThrowingSupplier<T> supplier) {
   //same logic as other one
   ...
}

where ThrowingSupplier is defined as

public interface ThrowingSupplier<T> {
   public T get() throws Exception;
}

In this way, we have one Supplier type which throws exceptions and one which doesn't. The desired syntax would be something like this:

foo(() -> operationWhichDoesntThrow()); //Doesn't throw, handled by Supplier
foo(() -> operationWhichThrows()); //Does throw, handled by ThrowingSupplier

However, this causes issues due to the lambda type being ambiguous (presumably unable to resolve between Supplier and ThrowingSupplier). Doing an explicit cast a la foo((ThrowingSupplier)(() -> operationWhichThrows())); would work, but it gets rid of most of the conciseness of the desired syntax.

I guess the underlying question is: if the Java compiler is able to resolve the fact that one of my lambdas is incompatible due to it throwing an exception in the Supplier-only case, why isn't it able to use that same information to derive the type of the lambda in the secondary, type-inference case?

Any information or resources which anyone could point me to would likewise be much appreciated, as I'm just not too sure where to look for more information on the matter.

Thanks!

Brian Goetz
  • 76,505
  • 17
  • 128
  • 138
paul
  • 641
  • 1
  • 5
  • 12
  • 2
    Your question is very similar to mine: http://stackoverflow.com/questions/14039995/java-8-mandatory-checked-exceptions-handling-in-lambda-expressions-why-mandato – Lyubomyr Shaydariv Sep 01 '15 at 05:49
  • Note of course that an implementation of an interface method need not throw an exception, even if the declaration in that interface is declared to throw the exception. So, with the lambda, it's not just that the throwing lambda "should" be determine to use the corresponding method, but the question of which method should the non-throwing lambda be passed to? I.e., `() -> nonThrowingOperation` could be *either* a `Supplier` or a `ThrowingSupplier`. – Joshua Taylor Sep 01 '15 at 15:57

3 Answers3

30

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.

Brian Goetz
  • 76,505
  • 17
  • 128
  • 138
  • Hey Brian, thanks for the response. That response makes sense, and reliability/precision definitely are reasonable to prioritize over concision. Do you by chance have an example of one of the cases where the method-body modification would silently change the overloading behavior of a method? It seems like in those cases, the body of the method enclosing those lambdas would need to change as well. I'm sure that I'm wrong, but I just can't wrap my head around a concrete instance just yet. – paul Sep 01 '15 at 04:27
  • This answer is good but I think it would be better with concrete examples as well as references to the JLS and the discussions that lead to this choice, if available. – Didier L Sep 01 '15 at 08:05
  • 1
    Not only should users be happy about it, also compiler engineers are very happy about the design being as it is :) – Stephan Herrmann Sep 05 '15 at 21:30
5

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 RuntimeException

foo( ()->{ 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` clause

static 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 erasure

static <T> void foo(S2<Result<T, ? extends RuntimeException>> s)
{
    System.out.println("s2");
}
ZhongYu
  • 18,232
  • 5
  • 28
  • 55
  • Method overloading is certainly not necessary, nor are lambdas. The question is more of "If I'm using lambdas, why can't I overload like this", which it looks like Brian has addressed above. Also, handling exceptions internally within the lambdas is the desire here. For example, imagine a function `resultOrNull`, returning the result of an expression, or null if an exception occurs: `resultOrNull(()->nullFile.getClass())` should return null. Also, there is no exception signature. `resultOrNull(()->nullFile.getCanonicalFilePath())` should also be null, but an exception signature is needed. – paul Sep 01 '15 at 04:49
  • 1
    well you can't compare adding an `X` suffix (my personal convention) to the inconvenience of not having lambda. – ZhongYu Sep 01 '15 at 04:54
  • Certainly not, I just meant to say that that wasn't exactly the question at hand. Multiple methods is definitely a way to solve the problem (and probably the one I'll go with, now knowing that inference isn't an option here). However, the question was more along the lines of *why* inference isn't possible. – paul Sep 01 '15 at 04:55
  • 1
    I don't understand your `nullFile` example. I imagine that you can have one `resultOrNull( ThrowingSupplier )` method, and the method applies to non-throwing lambda bodies too. – ZhongYu Sep 01 '15 at 04:57
  • 2
    look at the methods in [Comparator](https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html) - in early days of java8, they were overloaded; but they later simplified the language spec, and overloading no longer works, so we have odd things like `comparing`, `comparingInt`, `comparingLong` etc. – ZhongYu Sep 01 '15 at 04:59
  • Hey @bayou.io, the file example was meant to illustrate the desired behavior, but I can see why it'd be confusing. One could absolutely use only the `resultOrNull(ThrowingSupplier)` definition, but the downside is that if we want any checked exceptions to be thrown from `resultOrNull`, they would then have to be labelled on the `resultOrNull` method, even if the lambda passed in couldn't *possibly* throw them (i.e. a method which doesn't throw anything). For example, `resultOrNull(() -> 6)` would need to handle a thrown Exception (even though `6` cannot throw). – paul Sep 01 '15 at 06:15
  • 1
    @paul - there's a solution for that:) see http://stackoverflow.com/q/30759692/2158288 – ZhongYu Sep 01 '15 at 06:18
  • 1
    @paul - wait, that's terrible example. see my edit to this answer instead. – ZhongYu Sep 01 '15 at 06:32
  • Hey @bayou.io: that's fantastic! It's still a bummer that the types can't be inferred, but your solution provides an excellent workaround! I think that'll be the go-to workaround at this point, in fact; it provides all the nice syntax with pretty robust checked exception handling. Thanks for the tip! – paul Sep 01 '15 at 06:55
2

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());
}
augray
  • 2,685
  • 1
  • 13
  • 26
  • Hey augray, thanks for the advice. Unfortunately, while that would work, it does away with the benefits of the conciseness of lambdas. Solutions from `fooChecked(()->getAnotherString())` to `foo((ThrowingSupplier)(()->getAnotherString())` to `foo(()->wrap(getAString()))` all work, but they do away with the pure conciseness of the desired syntax. Additionally, I don't see any valid reason why the appropriate type can't be inferred. Thanks for your time, though, your way definitely is functional! – paul Sep 01 '15 at 04:03
  • 1
    @paul: See the edit I just made- using a `ThrowingSupplier` as the input for foo leaves you without any requirement to wrap methods, keeping the nice concise lambda syntax. (new stuff in top code sample) – augray Sep 01 '15 at 04:05
  • Thanks again! Unfortunately, the downside of this approach is that it makes the scope which encloses the non-throwing method invocation handle an Exception (which isn't possible). For example, the above version compiles because `main` throws Exceptions. If my function's purpose was, for example, to catch and swallow exceptions, making the invocation of the method handle exceptions would seem counterintuitive. Again, though, thanks for the input, and that way definitely works for cases where exception throwing is ok. – paul Sep 01 '15 at 04:10
  • @paul: main doesn't actually need to throw exceptions- that was left over from before. See the newest edit :-) . You could catch and handle the exceptions in `foo` if you needed to, and never let them escape to the outer scope of `main`. – augray Sep 01 '15 at 04:15
  • the only reason which an exception doesn't need to be caught in the above example is that the `ThrowingSupplier` is never invoked in the above `foo` method. As soon as that's done, the exception could be thrown, and needs to be caught. Thanks! – paul Sep 01 '15 at 04:23
  • @paul: If that's not okay, then what would you like to happen? If a checked exception is thrown, it has to be handled *somewhere*. Either in `foo` or some wrapper or some external scope. – augray Sep 01 '15 at 04:26
  • Hey augray, just to sum up the issue and desired behavior here: `foo(()->nonThrowingOp())` should not throw an exception, and the enclosing block should not need to catch an exception. This is not the case with the code above. `foo(()->throwingOp)` could either catch the exception in the lambda, in which case `foo` wouldn't throw anything, or the lambda could throw the Exception, in which case `foo` would either handle or throw the exception itself. In no case, though, would `foo(()->nonThrowingOp())` be marked as `throws Exception`. – paul Sep 01 '15 at 04:30
  • @paul: If you are **only** going to be using methods which don't throw (like `nonThrowingOp()`), then just use `Supplier` as the type for `foo` to accept. If you **ever** need to pass in a lambda which might throw a checked exception, then you'll need to have a way to handle said checked exception. – augray Sep 01 '15 at 04:33
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/88436/discussion-between-paul-and-augray). – paul Sep 01 '15 at 04:41