Retiring Checked Exceptions Was: Throwing Functions

Remi Forax forax at univ-mlv.fr
Sat Nov 12 07:58:57 UTC 2022


[promoted to amber-spec-experts] 

I think we should tackle this problem at its core and retire the notion of checked exceptions 

The written are on the wall since quite some time, but we have not yet acknowledge it. 

Let's recap the issue and the clues pointing in that direction. 

I believe the first straw can be tracked back to the inability of the type system to create a type variable containing the union of exceptions when generics where introduced in Java 5. 
It's the reason why there is no ThrowableConsumer, as an example, imagine that a stream can declare a checked exception Stream<T, E> and a user call map() that takes a ThrowableFunction, 
we need a way to express that the resulting stream is that combine the exceptions of the original stream and the exceptions that may be raised by calling the function of map. 

Stream<T, E extends Exception> --> map(ThrowableFunction<T, R, F extends Exception>) --> Stream<R, E | F> 

Or perhaps from the beginning of Java, adding a checked exception to a "throws" colors the function the same way the keyword async or Rust lifetime type information color a function [1]. Adding a checked exception to a function is not a backward compatible change. 

So we have some of idioms that beginners need to internalize to try workaround checked exceptions, 
- The oldest is i think, Thread.currentThread().interrupt() which allows to silence an InterruptedException at the price of continuing to execute the code until the next blocking call 
- UncheckedIOException and IOError that both wrap an IOException into an unchecked exception allowing tunneling of checked exceptions ; wrapping an IOException and unbundle it later. 
- Unsafe.rethrow (this one was retired) 
- the unfamous rethrow using erasure to see a checked exception as an unchecked exception 
static <T extends Throwable> AssertionError rethrow(Throwable cause) throws T { 
throw (T) cause; 
} 
- IntelliJ has changed the default try/catch snippet to instead of calling printStackTrace() to throw a RuntimeException wrapping the exception. 
This simple change is i believe the best change to Java in the recent years (perhaps toes to toes with records), at least now the code of my students does not print the stack trace and resume its course when an exception occurs. 
try { 
... 
} catch(FooException e) { 
throw new RuntimeException(e); 
} 

Also no language presented as potential successor of Java, neither Scala nor Kotlin have checked exceptions, because functions with checked exceptions do not compose. 
If Java wants to be the next Java, we will have to drop checked exceptions at some point. 

The good news is that seeing all exceptions as unchecked exceptions is a backward compatible change, "throws" can still be supported for documentation purpose, the compiler can emit a warning instead of an error if there is no catch corresponding to a checked exception and allow everyone to catch any exceptions in the code. 

I think we should recognize that the idea of checked exceptions was a good idea on paper but not a good one in practice and work to retire the concept of checked exceptions from Java. 

Rémi 

[1] https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ 

> From: "Nathan Reynolds" <numeralnathan at gmail.com>
> To: "amber-dev" <amber-dev at openjdk.org>
> Sent: Saturday, November 12, 2022 5:47:10 AM
> Subject: Throwing Functions

> I am sorry if I am very late to the discussion. Consumer, Function, Predicate,
> and Supplier don't allow for throwing checked exceptions. This feature is
> needed in many cases. What about adding a variant that allows for throwing
> checked exceptions? For example, a ThrowableConsumer could be defined as
> such...

> public interface ThrowableConsumer<T, E extends Throwable>
> {
> void accept(T t) throws E;
> }

> A method that receives this as a parameter could be defined as such...

> public <E extends Throwable> void process(ThrowableConsumer<T, E> consume)
> throws E
> {
> ...
> }

> The compiler takes care of ensuring the checked exception is dealt with in the
> caller to process().
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-spec-observers/attachments/20221112/70800cb8/attachment-0001.htm>


More information about the amber-spec-observers mailing list