문제

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.)

도움이 되었습니까?

해결책

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.
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top