2

It is designed in Java that I get a Throwable object if I invoke getCause() on an Exception.

I understand that getCause() is simply inherited from Throwable and I know that Throwable can be either an Error or an Exception, but a programmer generally should work only on the Exception level without dealing with Throwable/Error classes.

What was the reason in the Java exception hierarchy design to, for example, not include getCause() in the Exception class that would return an Exception object?

Here is an illustration of the inconvenience taken from Java Concurrency In Practice (Brian Goetz):

public class Preloader {
    private final FutureTask<ProductInfo> future =
        new FutureTask<ProductInfo>(new Callable<ProductInfo>() {
            public ProductInfo call() throws DataLoadException {
                return loadProductInfo();
            }
        });
    private final Thread thread = new Thread(future);
    public void start() { thread.start(); }
    public ProductInfo get()
        throws DataLoadException, InterruptedException {
        try {
            return future.get();
        } catch (ExecutionException e) {
            Throwable cause = e.getCause();
            if (cause instanceof DataLoadException)
                throw (DataLoadException) cause;
            else
                throw launderThrowable(cause);
        }
    }
}

It is said in the book:

...but also because the cause of the ExecutionException is returned as a Throwable, which is inconvenient to deal with...

And in launderThrowable() it is dealt with rethrowing Error right away (because we don't want to deal with it) and returning RuntimeException:

public static RuntimeException launderThrowable(Throwable t) {
    if (t instanceof RuntimeException)
        return (RuntimeException) t;
    else if (t instanceof Error)
        throw (Error) t;
    else
        throw new IllegalStateException("Not unchecked", t);
}
RealSkeptic
  • 33,993
  • 7
  • 53
  • 79
Ruslan
  • 3,063
  • 1
  • 19
  • 28
  • 3
    Because the root cause could be an `Error`. – RealSkeptic Aug 05 '15 at 16:44
  • I am asking why it is designed in such a way that I have to deal with Error. – Ruslan Aug 05 '15 at 16:54
  • Counter-question: Why is it so important to you that it would be an `Exception`, when all the interesting methods are in `Throwable`, like `printStackTrace()` etc.? – RealSkeptic Aug 05 '15 at 17:01
  • The question is only about why do I even need to see/deal anything other than Exception. Probably I'm just missing some idea here:) – Ruslan Aug 05 '15 at 17:22
  • @Ruslan I don't get what's confusing to you. `Error`s can happen anywhere, and one way of dealing with them is to wrap them in an `Exception`, which will then have an `Error` as its cause. – Louis Wasserman Aug 05 '15 at 18:17
  • I'm only dreaming:-), but probably I'd like Error to be just thrown in places where it is wrapped in an Exception:) – Ruslan Aug 08 '15 at 09:58

3 Answers3

4

getCause is a method defined in Throwable, and simply inherited in Exception. The cause of a Throwable is simply a Throwable (it could be an Exception or an Error).

IMO, having a method, say getCauseAsException, that just returns an Exception if any as the cause exception is not really useful. You could simply invoke getCause() and check if it's an instance of Exception if you're just concerned with Exception and not Errors.

M A
  • 71,713
  • 13
  • 134
  • 174
  • I know that:-) My question is about "why such a design is chosen", not "how this design works". I updated my question to be more clear. – Ruslan Aug 05 '15 at 16:53
  • 1
    @Ruslan What's the point of having a method that just gets the `Throwable` cause only if it is an `Exception`? Do you have a real case for that? I edited the answer BTW. – M A Aug 05 '15 at 16:57
  • Added a case in my question – Ruslan Aug 05 '15 at 17:21
  • "You could simply invoke getCause() and check if it's an instance of Exception if you're just concerned with Exception and not Errors." - I know there are ways to handle it, my question is more about why I should handle it rather than just getting an Exception. – Ruslan Aug 05 '15 at 17:24
2

First, if you have a type that has a few subtypes, and their behavior is really the same, it makes sense to define the methods at the parent class. Basically, what the designers are saying is: "Throwable can have a cause, which is also a throwable, and you can get that cause". And you can do that in both Exception and Error because they both happen to be throwables.

Now, the Throwable hierarchy exists since Java 1.0, and generics didn't exist there. Nowadays you might be able to have defined the behavior like this:

class ModernThrowable<T extends ModernThrowable<T>> {
    T getCause() {...}
}

And you could define ModernException as extends ModernThrowable<ModernException> and then you could get the sort of behavior that you expect.

But this design didn't exist back then, so you get a Throwable back, and you have to cast it. That's how things used to work and you have to keep backward compatibility.

But actually... as plausible as this may sound, this is not true. At least, it's not the whole truth.

It is perfectly OK to get an Error as the cause of an Exception. Take a look at javax.management.RuntimeErrorException. When you are working with an agent, you might get an Error, which in these special circumstances, should not cause you to abort the system immediately. So you put it as the cause of this special Exception. And thus, your getCause() will actually return an Error from an Exception.

So if it wasn't designed like this, you would have to go through hoops - have a specialty method just for the cause of this particular exception.

RealSkeptic
  • 33,993
  • 7
  • 53
  • 79
  • 1
    `class ModernThrowable>` might be possible, but [that won't happen](http://stackoverflow.com/questions/501277/why-doesnt-java-allow-generic-subclasses-of-throwable) :D. – Tom Aug 05 '15 at 17:48
  • @Tom Well, that's another reason, I guess. :-) – RealSkeptic Aug 05 '15 at 17:51
  • This looks to be the bast answer. How can I accept it on stackoverflow? – Ruslan Aug 13 '15 at 11:31
  • @Ruslan - There is a checkmark near the vote counter on the left side of the answer, which you can click to accept an answer. – RealSkeptic Aug 13 '15 at 11:33
0

Programmers generally work at the Exception level when they are implementing applications. But remember that the JVM, for instance, is also implemented in Java and, in this context, programmers should work at the Throwable/Error level too.

So the question of at which level in the exception hierarchy tree you should work its more of an issue of the domain of the system you're implementing, than an issue of the language design itself. So it makes perfect sense from a language design perspective to provide the getCause method in the Throwable class, which is the root of the hierarchy tree. Since the Exception class inherits this method from its superclass, it's not necessary to provide the same method with a different return type just because in some specific domains you don't/shouldn't work with Throwable instances.

EijiAdachi
  • 441
  • 1
  • 3
  • 15