Exception handling in switch (Preview)

Remi Forax forax at univ-mlv.fr
Mon Apr 22 09:15:13 UTC 2024


I agree with Tagir here, 
a lot of exceptions come from the fact that we do not have pattern methods yet (Integer.parseInt, etc) and a try expression (try-with-resources expression, try-catch expression or try-finally expression) seems to compose better than a "case throws". 

regards, 
Rémi 

> From: "Tagir Valeev" <amaembo at gmail.com>
> To: "Angelos Bimpoudis" <angelos.bimpoudis at oracle.com>
> Cc: "amber-spec-experts" <amber-spec-experts at openjdk.java.net>
> Sent: Saturday, April 20, 2024 10:00:48 AM
> Subject: Re: Exception handling in switch (Preview)

> Dear experts,
> looking into this proposal, I'm really not convinced that Java needs it. We
> already have try-catch statements, and it sounds strange to provide another way
> to express the same semantics. I don't see what the new construct adds, aside
> from a bit of syntactic sugar. On the other hand, it creates a new source of
> subtle bugs, especially when exceptions are unchecked. E.g., consider:

> switch(a.b().c().d()) {
> case ...
> case throws RuntimeException ex -> handle(ex);
> }

> Now, one may want to refactor the code, extracting a.b(), a.b().c(), or the
> whole a.b().c().d() to a separate variable for clarity, or to avoid a long
> line.
> This action is usually safe, and it was totally safe in switches so far (even
> with patterns and case null). Now, it's not safe, as exceptions thrown from the
> extracted part are not handled by the 'case throws' branch.
> I don't see a good way to perform this refactoring in a semantically equivalent
> way. The only possibility I see is to duplicate the exception handler in the
> external catch:

> try {
> var ab = a.b();
> switch(ab.c().d()) {
> case ...
> case throws RuntimeException ex -> handle(ex);
> }
> }
> catch(RuntimeException ex) {
> handle(ex); // duplicated code
> }

> As switch selector does not allow using several expressions or to declare new
> variables, extract/inline refactorings can easily become very painful, or cause
> subtle bugs if not performed correctly.
> Note that it's not a problem inside usual try-catch statement (*), as you can
> easily add or remove more statements inside the try-body.

> (*) Except resource declaration, but it's rarely a problem, and in some cases
> it's still possible to extract parts as separate resources, because you can
> declare several of them

> I think, instead of repurposing switch to be another form of try-catch we could
> add more love to try-catch allowing it to be an expression with yields in
> branches. The proposed JEP allows something like this:

> Integer toIntOrNull(String s) {
> return switch(Integer.parseInt(s)) {
> case int i -> i;
> case throws NumberFormatException _ -> null;
> }
> }

> But we are still limited by a single expression in the selector. An alternative
> would be
> Integer toIntOrNull(String s) {
> return try { yield Integer.parseInt(s); }
> catch(NumberFormatException _) { yield null; };
> }
> Here, all kinds of refactorings are possible. And we actually don't need to
> express pattern matching, because we essentially don't need any pattern
> matching.

> Also, note that some of the situations which are usually solved with exception
> handling in modern Java (e.g. Pattern.compile -> PatternSyntaxException, or
> UUID.fromString -> IllegalArgumentException, or Integer.parseInt above) will be
> covered in future by member patterns. So probably if we concentrate more on
> member patterns, people will need much less exception handling in business
> logic, and such an enhancement will be not so useful anyway? Speaking about the
> sample from the JEP, can we imagine something like this in the future (sic!)
> Java?

> switch(future) {
> case Future.cancelled() -> ...
> case Future.interrupted() -> ...
> case Future.failed(Exception ex) -> ... // no need to unwrap ExecutionException
> manually
> case Future.successful(Box b) -> ...
> }

> One more note about the JEP text. It's unclear for me whether 'case throw'
> branches could catch a residual result. More precisely, if MatchException
> happens, or NullPointerException happens (selector evaluated to null, but
> there's no 'case null'), can these exceptions be caught by the 'case throws'
> branches in the same switch?

> With best regards,
> Tagir Valeev.

> On Fri, Apr 19, 2024 at 3:05 PM Angelos Bimpoudis < [
> mailto:angelos.bimpoudis at oracle.com | angelos.bimpoudis at oracle.com ] > wrote:

>> Dear spec experts,

>> A while ago we discussed on this list about enhancing the switch ​ construct to
>> support case ​ labels that match exceptions thrown during evaluation of the
>> selector expression. A draft JEP for this feature is now available at:

>> [ https://bugs.openjdk.org/browse/JDK-8323658 |
>> https://bugs.openjdk.org/browse/JDK-8323658 ]

>> Please take a look at this new JEP and give us your feedback.

>> Thanks,
>> Aggelos
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-spec-observers/attachments/20240422/fc09c73a/attachment-0001.htm>


More information about the amber-spec-observers mailing list