23

Reading the JavaDoc of Optional, I bumped in a weird method signature; I never saw in my life:

public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier)
                                throws X extends Throwable

At first glance, I wondered how the generic exception <X extends Throwable> is even possible, since you can't do that (here, and here). On second thought, this starts to make sense, as it is here just to bind the Supplier... but the supplier itself knows exactly what type it should be, before generics.

But the second line hit me:

  • throws X is a complete generic exception type.

And then:

  • X extends Throwable, what in the world does this mean?
    • X is already bound in the method signature.
  • Will this by any means, solve the generic exception restriction?
  • Why not just throws Throwable, as the rest will be erased by type erasure?

And one, not directly related question:

  • Will this method be required to be caught as catch(Throwable t), or by the provided Supplier's type; since it can't be checked for at runtime?
Community
  • 1
  • 1
Mordechai
  • 13,232
  • 1
  • 32
  • 69
  • 4
    You can't have generic `Exception` types, but you can bind `Exception` types to generic parameters. – Sotirios Delimanolis Jun 10 '15 at 14:43
  • Also, that isn't a source code declaration, it's some bastardization (of the bounds) in the javadoc. – Sotirios Delimanolis Jun 10 '15 at 14:45
  • @SotiriosDelimanolis I pointed that out, already. – Mordechai Jun 10 '15 at 14:47
  • Source is here http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/java/util/Optional.java#Optional.orElseThrow%28java.util.function.Supplier%29 – Shaun Jun 10 '15 at 14:57
  • 2
    @MouseEvent Please don't keep adding questions to your original question that make answers incomplete / invalid – durron597 Jun 10 '15 at 15:13
  • It is allowed to have a type parameter to subclass throwable. What is to have a generic exception `MyException extends Throwable`. (Your links dont say that either). – eckes Jun 11 '15 at 01:49

4 Answers4

17

Treat it like any other generic code you've read.

Here's the formal signature from what I see in Java 8's source code:

public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X
  • X has an upper bound of Throwable. This will be important later on.
  • We return a type T which is bound to Optional's T
  • We expect a Supplier which has a wildcard upper bound of X
  • We throw X (which is valid, since X has an upper bound of Throwable). This is specified in JLS 8.4.6; so long as X is seen as a subtype of Throwable, its declaration is valid and legal here.

There is an open bug about the Javadoc being misleading. In this case, it's best to trust the source code as opposed to the documentation until the bug is declared fixed.

As for why we're using throws X instead of throws Throwable: X is guaranteed to be bound to Throwable in the uppermost bounds. If you want a more specific Throwable (runtime, checked, or Error), then merely throwing Throwable wouldn't give you that flexibility.

To your last question:

Will this method be required to be caught in a catch(Throwable t) clause?

Something down the chain has to handle the exception, be that a try...catch block or the JVM itself. Ideally, one would want to create a Supplier bound to an exception that best conveyed their needs. You don't have to (and probably should not) create a catch(Throwable t) for this case; if your Supplier is type bound to a specific exception that you need to handle, then it's best to use that as your catch later up in the chain.

Makoto
  • 96,408
  • 24
  • 164
  • 210
  • I made a minor typo in my last bullet, please review it. – Mordechai Jun 10 '15 at 15:08
  • And added another question, thereafter. – Mordechai Jun 10 '15 at 15:10
  • 2
    Be **careful** about adding more than one question to a question. I've addressed the earlier concern, and I'll be happy to review the revised bullet, but the second question is overreaching. I'll add that part in a bit later. – Makoto Jun 10 '15 at 15:11
  • For your last paragraph, How can I _want_ a more specific `Throwable`, do you mean by subclassing? Note that `Optional` is declared final. – Mordechai Jun 10 '15 at 15:35
  • Your `Supplier` instance is providing the exception you want to throw to begin with; if that's type bound to something more specific like `NullPointerException`, you'd want to use that instead of `Throwable`. Or better yet - if you decided to throw an `Error` at this point, you would rather throw the `Error` than `Throwable`. – Makoto Jun 10 '15 at 15:42
  • This means, that a method that calls this, may `catch` it just with a `NullPointerException`? This is actually my last question. – Mordechai Jun 10 '15 at 15:50
  • Maybe I'm misunderstood, but can __this method itself__ be caught more specificly? If not why shouldn't I say `throws Throwable`? The only place this makes sense to me, is when overrided, but as I pointed out, `Optional` is `final`. (I keep on editing my last question, as it seems to mislead, sorry). – Mordechai Jun 10 '15 at 16:05
  • 2
    Understand two things: `X` dictates what we are going to throw, and `Supplier extends X>` specifies *what* that particular something is that we're going to throw. If your Supplier instance provides a specific type of exception, then that is to be caught as opposed to `Throwable`. – Makoto Jun 10 '15 at 16:07
  • @MouseEvent Yes, with the throws beeing typed to the return type of the supplier, the following method requires (and allows) a specific "`catch (InterruptedException)`": `opt.orElseThrow(()->new InterruptedException());`. It is independent of the type of opt (e.g. `Optional opt = new Optional<>();`) – eckes Jun 11 '15 at 02:09
11

The Javadoc's method signature is different than the source code. According to the b132 source:

public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier)
              throws X {  // Added by me: See? No X extends Throwable
    if (value != null) {
        return value;
    } else {
        throw exceptionSupplier.get();
    }
}

Confirmed: This is an issue with the Javadoc generation. As a test, I created a new project and generated Javadoc for the following class:

package com.stackoverflow.test;

public class TestJavadoc {
    public static <X extends Throwable> void doSomething() throws X {

    }
}

This is the resulting Javadoc:

Javadoc Screenshot

Double confirmed: There is an open bug about the Javadoc for this class

durron597
  • 30,764
  • 16
  • 92
  • 150
8

Java cannot catch generic exception types due to erasure

    catch(FooException<Bar> e)        --analogy->        o instanceof List<String>

catch and instanceof depend on runtime type information; erasure ruins that.

However, it's a little too harsh to forbid generic declaration of exception types like FooException<T>. Java could allow it; just require that only raw type and wildcard type are allowed in catch

    catch(FooException e)              --analogy->       o instanceof List
    catch(FooException<?> e)                             o instanceof List<?>

An argument can be made that, the type of an exception is mostly interested by catch clauses; if we cannot catch generic exception types, there's no point of having them in the first place. OK.


Now, we cannot do this either

    catch(X e)   // X is a type variable

then why does Java allow X to be thrown in the first place?

    ... foo() throws X

As we'll see later, this construct, aided by erasure, could actually subvert the type system.

Well, this feature is quite useful though - at least conceptually. We write generic methods so that we can write template code for unknown input and return types; same logic for throw types! For example

    <X extends Exception> void logAndThrow(X ex) throws X
        ...
        throw ex;

    ...
    (IOException e){   
        logAndThrow(e);   // throws IOException

We need to able to throw generically to achieve exception transparency when abstracting a block of code. Another example, say we are tired of writing this kind of boilerplate repeatedly

    lock.lock();
    try{ 
        CODE
    }finally{
        lock.unlock();
    }

and we want a wrapper method for that, taking in a lambda for the CODE

    Util.withLock(lock, ()->{ CODE });

The problem is, CODE could throw any exception type; the original boilerplate throws whatever CODE throws; and we want the writeLock() expression to do the same, i.e. exception transparency. Solution -

    interface Code<X extends Throwable>
    {
        void run() throws X;
    }



    <X extends Throwable> void withLock(Lock lock, Code<X> code) throws X
        ...
        code.run();  // throws X

    ...
    withLock(lock, ()->{ throws new FooException(); });  // throws FooException

This only matters for checked exceptions. Unchecked exceptions can propagate freely anyways.

One serious flaw of the throws X approach is that it doesn't work well with 2 or more exception types.


All right, that's really nice. But we are not seeing any of such usages in new JDK APIs. No functional types of any method parameter can throw any checked exception. If you do Stream.map(x->{ BODY }), BODY cannot throw any checked exceptions. They really really hate checked exceptions with lambdas.

Another example is CompletableFuture, where exception is a central part of the whole concept; yet, no checked exceptions are allowed either in lambda bodies.

If you are a dreamer, you might believe that in some future version of Java, an elegant mechanism of exception transparency for lambda will be invented, nothing like this ugly throws X hack; therefore we don't need to pollute our APIs with <X>s for now. Yeah, dream on, man.


So, how much is throws X used anyways?

Throughout the entire JDK source, the only public API that does that is Optional.orElseThrow() (and its cousins OptionalInt/Long/Double). That's it.

(There's a shortcoming with the orElseGet/orElseThrow design - the decision of branching cannot be done in the lambda body. We could instead design a more general method

    T orElse( lambda ) throws X

    optional.orElse( ()->{ return x; } );
    optional.orElse( ()->{ throw ex; } );
    optional.orElse( ()->{ if(..)return x; else throw ex; } );

Besides orElseThrow, the only other method in JDK that throws X is the non-public ForkJoinTask.uncheckedThrow(). It is used for "sneaky throw", i.e. a code can throw a checked exception, without the compiler and runtime knowing about it -

    void foo() // no checked exception declared in throws
    {
        throw sneakyThrow( new IOExcepiton() ); 
    }

here, IOException is thrown from foo() at runtime; yet the compiler and runtime failed to prevent it. Erasure is largely to blame.

public static RuntimeException sneakyThrow(Throwable t)
{
    throw Util.<RuntimeException>sneakyThrow0(t);
}
@SuppressWarnings("unchecked")
private static <T extends Throwable> T sneakyThrow0(Throwable t) throws T
{
    throw (T)t;
}
ZhongYu
  • 18,232
  • 5
  • 28
  • 55
3

There's a difference between generic types and generic variables/parameters.

A generic type is a type that declares a generic parameter. For example

class Generic<T> {}

What isn't allowed is a generic type (like above) that also extends Throwable.

class Generic <T> extends Throwable {} // nono

This is expressed in the Java Language Specification, here

It is a compile-time error if a generic class is a direct or indirect subclass of Throwable (§11.1.1).

Generic variables themselves, however, can have any bound, generic or otherwise, as long as it is a reference type.

This

X extends Throwable

means that whatever type is bound (not has bounds) to X at compile time must be a subtype of Throwable.

X is already bound in the method signature.

X is not bound. The method declares its bounds.

Will this by any means, solve the generic exception restriction?

No.

Why not just extends Throwable, as the rest will be erased by type erasure?

Generics are a compile time feature. Erasure happens after compilation. I believe they could have simply used

Supplier<? extends Throwable>

as the parameter type. But then you'd lose the type variable for use in the throws clause.

Post-edit:

Why not just throws Throwable, as the rest will be erased by type erasure?

You want the throws to use the same Exception type that the Supplier supplies.

Will this method be required to to be caught in a catch(Throwable t) clause?

No/depends. If you bind a checked exception to X, then you'll need to handle it some way. Otherwise, no catch is "required". But something will/should handle it eventually, even if it is the JVM itself.

Sotirios Delimanolis
  • 252,278
  • 54
  • 635
  • 683