12

I'd like to document an interface method in Java as not being allowed to propagate exceptions and have some sort of static analysis verifying that implementations of this method catch and handle any exceptions that may have propagated to it. Something along the lines of @NoThrow.

For example, I'd like to be able to write:

interface SomeServiceProviderInterface {
   @NoThrow
   @NonNull
   SomeResult someComputation();
}

... and have some guarantee that implementations obey this interface contract. Is there an annotation and static analysis tool that already does this? If not, does anyone know if this is achievable with an annotation processor (can it see whether the code includes a try...catch block?) and/or have any pointers or advice on how to go about implementing such a thing? Thanks!

Michael Aaron Safyan
  • 93,612
  • 16
  • 138
  • 200
  • 4
    I doubt that you can do this other than by catching Throwable with an empty catch block or close to it, since any significant code in the catch block will create objects, which can result in the throwing of an OutOfMemoryError. It's also probably the wrong thing to do. Some throwables, such as Error objects and some RuntimeException objects, are really designed never to be caught. – Warren Dew Sep 03 '15 at 01:08
  • 2
    @WarrenDew okay, for the sake of argument, assume that its really something more like "@ThrowsOnly(Error.class)" where serious errors like "Error" (and OutOfMemoryError) can be thrown, but I want to guarantee that subclasses do not propagate type "Exception"... any ideas on how this might be implemented? In this particular context, I really do want to do this. – Michael Aaron Safyan Sep 03 '15 at 04:44
  • Simply by not specifying a throws clause in the function signature, implementations will not be allowed to throw checked exceptions. – Warren Dew Sep 03 '15 at 16:15
  • Beyond that, you could probably write an annotation that would examine a function body and verify that no Exceptions were constructed, and maybe checked that there was no possibility of a null pointer exception, and also checked that all the functions called also had the same annotation, thus verifying them recursively. I question the usefulness since you would then not be able to use any libraries. Perhaps more useful would be to change your interface to an abstract class, and simply put the entire function body into a try/catch block. – Warren Dew Sep 03 '15 at 16:16
  • 2
    @WarrenDew Static knowledge that a method doesn't throw exceptions can be very valuable. It can greatly simplify calling code and have an influence on exception handling strategies. Omitting the `throws` clause is obviously no solution, as it still permits `RuntimeException`. I suggest you have a look at C++'s `noexcept` keyword, which was the conclusion of decade-long investigations of exception handling. There *are* language differences, but many of the C++ arguments apply to Java. Some even more, because Java's exception safety, `try-finally`/`try-with-resources`, is much weaker than RAII. – TheOperator Dec 17 '15 at 20:33
  • 1
    I think the correct way to implement this is not to have this checked via the annotation at runtime, but rather have annotations that indicate your intentions and have a static analysis tool verify that claim. Just like `@NonNull` is implemented in Eclipse/IntelliJ. – Alexander Oh Mar 25 '16 at 10:35

2 Answers2

5

There can be no such annotation, because it is impossible to guarantee that a method does not throw exceptions. This is because any method may throw a VirtualMachineError at anytime. In particular, a method could throw an OutOfMemoryError even if it does not directly or indirectly allocate memory itself (using a new operator). This is not a merely theoretical concern: some concurrent garbage collectors will do this if a garbage collection thread takes too long.

Raedwald
  • 46,613
  • 43
  • 151
  • 237
  • Definitely there CAN be such annotation. Its meaning gonna be like "does not emit java.lang.Exception". But this does not diminish its value in a practical sense. – Pavel Vlasov Feb 23 '23 at 09:51
2

An unsatisfactory answer

Exceptions are a valuable language element, they not only allow you to handle errors but also to shortcut a computation. Imagine you are searching for the shortest list in a highly branching recursive algorithm. When happening upon an empty list, you could immediately return a result by throwing an exception, signalling that an empty list was found.

And of course to skip a computation that is not feasible instead of bubbling up nulls is subroutines.

So this matter is on the level of avoiding many if-statements to facilitate testing (as otherwise untested code sections may be buried in the code's control flow).

Much effort would be class scanning for importing exception classes, and dangerous calls (Integer.parseInt). Some of that, code analyzers already warn against.

I assume that try {...} catch (Throwable/RuntimeException e) {} is just for some legacy cases. There AOP would help too.

The best alternative would be to integrate exceptions. There are evaluation libraries (I do not remember their names - maybe relating to continuations) that can handle exceptions and process mixed results. A bit like what Streams are doing now, where exceptions are handled partly asynchronously and such.

Jonny Henly
  • 4,023
  • 4
  • 26
  • 43
Joop Eggen
  • 107,315
  • 7
  • 83
  • 138