13

Some background, then some questions.

I have only recently discovered that an interface (or class) may be generic in the type of (checked) exception that its methods may throw. For example:

interface GenericRunnable<X extends Exception> {
    void run() throws X;
}

The point is if you later instantiate this with, say, IOException and invoke the run method, the compiler knows you need to either catch IOException or mark it as thrown. Better still, if X was a RuntimeException, you don't need to handle it at all.

Here's a contrived example using the above interface, but it's basically a callback and should be quite common.

public <X extends Exception> void runTwice(GenericRunnable<X> runnable) throws X {
    runnable.run(); runnable.run();
}
...
public myMethod() throws MyException {
    runTwice(myRunnable);
}

We're calling a generic utility method runTwice (perhaps defined in an external library) to run our own specific method with a specific checked exception, and we don't lose any information about which specific checked exception might be thrown.

The alternative would have been to simply use throws Exception on both the Runnable.run method and the runTwice method. This would not restrict any implementation of the Runnable interface but the advantage of checked exceptions would be lost. Or there could have been no throws at all, also losing the advantage of checked exceptions and potentially forcing the implementation to wrap.

Because I had never seen throws X, maybe I've missed something. Furthermore, I have seen the callback example used as an argument against checked exceptions several times without it being refuted. (This question is not interested in the pros/cons of checked exceptions.)

Is throws X generally a good idea? What are the pros and cons? Can you give some examples that either use throws X or didn't but should have?

Basically, I would like some further insight. You might comment on the following examples.

  • OutputStream throws IOException (perhaps ByteArrayOutputStream could extends GenericOutputStream<RuntimeException>)

  • Callable / Future.get

  • Commons Pool borrowObject / makeObject

(Since editing, I am not asking if these could/should have been designed differently in retrospect. Rather, would throws X be better than throws Exception.)

user2357
  • 452
  • 5
  • 11
  • 3
    The stream classes couldn't have been designed like that, because they were designed before this feature was added (i.e., generics). – tbodt Feb 09 '14 at 00:57
  • Guava's [`Throwables`](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/Throwables.html) has some examples. But this question seems too broad. Try to refine your various questions throughout the post into something more focused. – Paul Bellora Feb 09 '14 at 01:49
  • Yep. Java has evolved over time. That means there are always going to be places in the older classes/feature where the newer ones _could_ have been used, but weren't available -- and since things work well enough without them, there hasn't been sufficient justification for doing so. You can of course implement your own wrappers/reinventions of the older classes to use the new features, and if they actually prove useful enough they may be adopted; that is in fact part of how the Java libraries have grown. But that happens only there's enough real added value, and enough demand, to justify it. – keshlam Feb 09 '14 at 01:52
  • @keshlam And "no breakage" when done so .. – user2864740 Feb 09 '14 at 02:21
  • One downside of generics is that you need to design classes and APIs very careful so that you can keep the type around. Put more than 1 Generic into a list with other Generic and you've lot it. I think that would in many cases mean that you could not actually use the type but would have to deal with Generic>. – zapl Feb 09 '14 at 23:04

1 Answers1

3

I use this pattern all the time, mostly for functional-style java.

Pros:

  1. You can have several flavors of a higher-order pattern like Visitor, to wit:

    interface ExceptionalVoidVisitor< E extends Exception > {
        void handleA( A a ) throws E;
        void handleB( B b ) throws E;
    }
    interface VoidVisitor extends ExceptionalVoidVisitor< RuntimeException > {}
    interface ExceptionalVisitor< T, E extends Exception > {
        T handleA( A a ) throws E;
        T handleB( B b ) throws E;
    }
    interface Visitor< T > extends ExceptionalVisitor< T, RuntimeException > {}
    
  2. Your client can declare a base exception class for all the exceptions he might throw and you end up with a fully general library.

Cons:

  1. As you've discovered, there is no way to handle an exception of generic type; you must allow it to escape.
  2. The other option is to accept a generator of E, which can be awkward for clients.
Judge Mental
  • 5,209
  • 17
  • 22