Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper Design for Java Interfaces with Respect to Exceptions [closed]

I'm trying to understand how to properly define interfaces in Java with respect to how exceptions are treated in the language (and runtime).

NOTE: Perhaps this question has already been asked and adequately answered, but my search of SO (and other sites) didn't turn up anything that directly addressed the design question and the trade-offs I present. I'll happily delete this question if an analogous one can be found that answers my question. I apologize in advance for the long-winded question, but I wanted to state the problem as clearly as possible. I'm also aware of the controversy around checked exceptions, but since I am not in a position to change the language and must still work within it, I'd like to select the approach(es) that will cause the least pain in the long term.

The recurring problem I encounter is when I try to define my own interfaces I am faced with the choice of how to specify which (if any) exceptions are allowed to the members of the interface. The trouble is that I have no way of knowing which exceptions are meaningful, since I don't know ahead of time what all of the possible implementations for my interface might be. For the sake of an example, suppose we have the following interface:

// purely hypothetical interface meant to illustrate the example...
public interface SegmentPartition {
    // option 1
    bool hasSegment(Segment s);
    // option 2
    void addSegment(Segment s) throws Exception;
    // option 3
    bool tryAddSegment(Segment s) throws TimeoutException, IOException, ParseException, XmlException, SQLException;
    // option 4
    void optimizeSegments() throws SegmentPartitionException;
}

What exception types should this interface declare upfront? The options I see are:

  1. don't declare any exception, implementations can only throw RuntimeException's
  2. declare each method as throwing Exception
  3. try to anticipate every possible exception type that makes sense for this interface and declare them as the throwable exceptions.
  4. create my own exception type (e.g. SegmentException) and require that it be thrown with an inner exception as needed for additional detail.

There are problem with each of these approaches, and it's not entirely clear what all of the tradeoffs are likely to be, I've mentioned a few for each case. Ideally, I don't want any exceptions to be thrown by the implementation, but that's not always a practical restriction.

For option 1, the trouble is that many of the exception types that may be thrown are not derived from RuntimeException (for instance IOException or TimeoutException). I could, of course, add these specific exceptions to the interface and then allow other RuntimeExceptions to be thrown as well, but what about user-defined exceptions then? This seems like a poor option.

With option 2 we end up with an interface that seems like it could result in any exception whatsoever (which I suppose is true), and provides no guidance to implementors on what should actually be thrown. The interface is no longer self documenting. Worse yet, every call site of this interface must either be declared as throwing Exception or wrap each call to methods of this interface in try{} catch(Exception), or create a block that catches Exception and tries to suss out whether it was thrown by the interface implementation or something else in that block. Yuck. Alternatively, the code that uses this interface could catch the specific exceptions that the implementations might throw, but then it violates the purpose of having an interface to begin with (in that it requires the caller to know the runtime type of the implementation). In practice, this is the option closest to what most other languages I know take with regards to exceptions (C++, C#, Scala, etc).

With option 3 I must specifically anticipate the potential implementations that could be defined for my interface, and declare the exceptions most appropriate for them. So if I anticipate that my interface may be implemented over a remote, unreliable connection I would add TimeoutException, if I anticipate it may be implemented using external storage I would include IOException, and so on. I am almost certainly likely to get it wrong ... which means that I am going to continually churn the interface as new implementation paradigms surface (which, of course, breaks all existing implementations and requires them to declare non-sensical exceptions as being thrown). A further problem with this approach is that results in ugly, repetitive code ... particularly for large interfaces with many methods (yes, sometimes these are needed) which then need to be repeated at each implementation point.

With option 4 I must create my own exception type, together with all of the baggage that accompanies that (serializability, nesting and inner exceptions, coming up with a reasonable hierarchy, etc). What seems to inevitably happen here is that you end up with one exception type for each interface type: InterfaceFoo + InterfaceFooException. Aside from the code bloat this entails, the real problem is that the exception type doesn't mean anything ... it's just a type used to bundle together all of the error conditions for the particular interface. In some cases, perhaps, you can create a few subclasses of the exception type to indicate particular modes of failure, but even that is questionable.

So what is the right thing to do here, in general? I realize there may not be a cut and dry rule, but I'm curious about what experienced Java developers choose do.

Thanks SO.

like image 793
LBushkin Avatar asked Nov 30 '13 20:11

LBushkin


People also ask

Should interfaces throw exceptions?

Yes, the abstract methods of an interface can throw an exception.

Which is the valid example of interface implementation?

Explanation: Concrete class implements an interface.

Which of the following options best describe an interface in Java?

An interface is a reference type in Java. It is similar to class. It is a collection of abstract methods. A class implements an interface, thereby inheriting the abstract methods of the interface.

How do we solve the problem of adding a new method in an interface without breaking all the child classes that implement the interface?

If the module containing the interface is compiled at a different time than the module containing the implementation, you can add a new method to the interface without it being aware that it's breaking an external module.


2 Answers

Having throws Exception (it it's a checked exception) in an interface means you know under what condition such a checked exception should be thrown. That means that interface already has some knowledge of its implementation. Which should not be the case. Therefore I would go against any throws Exception in an interface. If an interface throws a checked Exception I would suggest simplifying it leaving exception logic to the implementation(s).

like image 82
Alexander Kulyakhtin Avatar answered Oct 25 '22 13:10

Alexander Kulyakhtin


A possible simple answer is - one should declare those exceptions which can be and typically will be processed during execution - i.e. to propagate, log or quit after an exception. Apart from an being error, I think that an exception is an unconditionally returned result type.

If the interface will be used internally, i.e. in a database, one possible way is to throw those exceptions that you specifically will/are processing.

If it's a public library, i.e. ExecutorService, some deep analysis can be required.

Example - JavaDoc from ExecutorService:

 /**
 * ...
 * @return the result returned by one of the tasks
 * @throws InterruptedException if interrupted while waiting
 * @throws TimeoutException if the given timeout elapses before
 *         any task successfully completes
 * @throws ExecutionException if no task successfully completes
 * @throws RejectedExecutionException if tasks cannot be scheduled
 *         for execution
 */
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
                long timeout, TimeUnit unit)
    throws InterruptedException, ExecutionException, TimeoutException;

All these exceptions are domain-specific and one can may decisions based on their general meaning. So you can actually guess what they mean in the context of invokeAny method given only this method's description.

This means that a couple of prototype implementations can be required to understand what your domain is, what are the typical use cases and which typical errors can be thrown. I don't know how Java libraries were designed, in my case I do many refactorings for my prototype implementations and by doing this I understand the domain.

Some more points:

  • Java's libraries document runtime exception which can be thrown, so it's perfectly fine to have them.
  • Thus, implementing classes can throw a domain specific runtime exception if you forget one of the cases.
  • One may document to wrap undeclared exceptions into one of the returned type exceptions, i.e. into ExecutionException - this is how it's done for java.util.concurrent.
like image 38
Andrey Chaschev Avatar answered Oct 25 '22 13:10

Andrey Chaschev