Would We Still Criticise Checked Exceptions, If Java had a Better try-catch Syntax?

In the context of a previous blog post about JUnit 5, Maaartinus, one of our readers, has brought up a very interesting idea:
The only problem with try-catch is its verbosity, which is something I can live with (IMHO a lone catch would do better, the implicit try would apply to all preceding code in the block; just syntactic sugar)
Huh! Imagine a world where the following is valid Java code:

{
    something();
}
catch (Exception e) {
    /* All exceptions from the above block */
}

Likewise:

{
    something();
}
finally {
    /* Clean up after the previous block */
}

In other languages, this is implemented exactly as such. Take PL/SQL, for instance. An ordinary block looks like this:

BEGIN
  SOMETHING();
END;

Replace curly braces by BEGIN and END keywords, and you have exactly the same thing. Now, if SOMETHING raises an exception, in PL/SQL, we can append an EXCEPTION block, which does exactly the same thing as catch in Java:

BEGIN
  SOMETHING();
EXCEPTION
  WHEN OTHERS THEN NULL;
END;

Indeed, in these very trivial cases, the try keyword seems optional just like there is no such keyword in PL/SQL, and we don’t really need it as the scope of the catch and/or finally blocks is very well defined (at first sight, there might be caveats, of course).

So what? We’ve saved 3 characters…

In these trivial cases, we’re not gaining a lot from the “improved” syntax. But what about many other cases where the notoriously verbose try { ... } catch { ... } syntax might be getting on our nerves…? Again, in PL/SQL, whenever you’re using a block using BEGIN .. END, you can automatically profit from optionally adding an EXCEPTION block. Without thinking this through thoroughly though (whew, some English language usage!), this could add immense syntactic value to Java. For instance: lambdas

// Better:
Consumer<String> consumer = string -> {
    something();
}
catch (Exception e) {
    /* still part of the consumer */
}

// Instead of:
Consumer<String> consumer = string -> {
    try {
        something();
    }
    catch (Exception e) {
        /* still part of the consumer */
    }
}

Would that have prevented long discussions about checked exceptions in lambdas and in the Stream API loops

// Better:
for (String string : strings) {
    something();
}
catch (Exception e) {
    /* still part of the loop's iteration */
}

// Instead of:
for (String string : strings) {
    try {
        something();
    }
    catch (Exception e) {
        /* still part of the loop's iteration */
    }
}

Again, tons of syntactic value here! if / else For consistency reasons, although this might appear a bit esoteric to people used to Java code. But let’s think out of the box, and admit the following!

// Better:
if (check) {
    something();
}
catch (Exception e) {
    /* still part of the if branch */
}
else {
    somethingElse();
}
catch (Exception e) {
    /* still part of the else branch */
}

// Instead of:
if (check) {
    try {
        something();
    }
    catch (Exception e) {
        /* still part of the if branch */
    }
}
else {
    try {
        something();
    }
    catch (Exception e) {
        /* still part of the else branch */
    }
}

Huh! method bodies Last but not least, method bodies would be the ultimate entities profiting from this additional syntax sugar. If you’re admitting that the curly braces in methods are nothing but mandatory blocks (or mandatory BEGIN .. END constructs), then you could have:

// Better:
public void method() {
    something();
}
catch (Exception e) {
    /* still part of the method body */
}

// Instead of:
public void method() {
    try {
        something();
    }
    catch (Exception e) {
        /* still part of the method body */
    }
}

This is particularly useful for (static) initialisers, where exception handling is always a pain, as there is no way to specify a throws clause in a (static) initialiser (might be a good opportunity to fix that!)

class Something {
    
    // Better:
    static {
        something();
    }
    catch (Exception e) {
        /* still part of the initialiser body */
    }

    // Instead of:
    static {
        try {
            something();
        }
        catch (Exception e) {
            /* still part of the initialiser body */
        }
    }
}

Take this one step further

Of course, we wouldn’t stop here. We’d also get rid of the very peculiar requirement of putting curly braces after catch (or finally). Once we have established the above, how about also allowing:

// Better:
something();
    catch (SQLException e)
        log.info(e);
    catch (IOException e)
        log.warn(e);
    finally
        close();

// Instead of:
try {
    something();
}
catch (SQLException e) {
    log.info(e);
}
catch (IOException e) {
    log.warn(e);
}
finally {
    close();
}

Now, make exception blocks expressions, rather than statements, and suddenly, Java starts to look an awful lot like all those cool languages. Like Scala. Or Kotlin.

Conclusion

Of course, the “old” syntax would still be possible. For instance, when using the try-with-resources statement, it is inevitable. But the big advantage of such syntax sugar is that in cases when we have to handle exceptions (namely checked exceptions), the pain would be lessened a bit, as we could do so without nesting blocks several levels deep. Perhaps, with this syntax, we would no longer criticise checked exceptions at all? Very interesting ideas, thanks again, Maaartinus, for sharing. What are your thoughts?

17 thoughts on “Would We Still Criticise Checked Exceptions, If Java had a Better try-catch Syntax?

  1. Ye gods and little fishes, I can’t decide if this is beautiful or horrifying. What about scope? If you can put a catch() after a method body, is ANYTHING in the method still in scope in the catch? I would guess not… but that means that if I need something in the catch(), now the “something” has to be a field in the class itself… leakage, leakage everywhere. Ewww.

    1. If you can put a catch() after a method body, is ANYTHING in the method still in scope in the catch?

      Yes. Look at it like the synchronized modifier, which is roughly the same as an immediately enclosed synchronized block. It would just be syntax sugar. Admittedly, a bit of an esoteric type.

      Or, in PL/SQL:

      PROCEDURE proc IS        // void proc()
      BEGIN                    // {
        something();           //   something();
      EXCEPTION                // } catch 
        WHEN OTHERS THEN NULL; //     (Exception e) {
      END proc;                // }
      
  2. Interesting idea. However, checked exceptions violate modularity — no amount of syntax sugar is going to change that, ever. We are using exceptions instead of error return codes because exceptions can be ignored everywhere except where they can be handled meaningfully. Whether that’s the case can only be determined on the calling side, but with checked exceptions, the callee forces the caller into handling the exception (be it with a catch or a throwing declaration).

    Stay strong and say no to checked exceptions, no matter how much candy Lukas is offering! ;-)

    1. Hah, yes. Interesting thought. But at least, wrapping the checked exception in an unchecked one will become easier, and there are legacy checked exceptions – you cannot deny that :)

      1. Neither I can deny that, but I can wrap and forget them. I love every useful piece of statically available information, but I hate checked exceptions. Conclusion: They are not useful. (or I’m plain wrong :D)

        They’re the reason for so many swallowed exceptions. Because of them, we won’t ever have things like CloseableIterable:

        for (String line : file.lines(Charsets.UTF_8)) {…}

        The CloseableIterator would get closed, when the loop gets left (normally or not) and the exception would get propagated to where someone can handle it. No catch, no finally, not even try-with-resources needed here (it’s implicit in the “once more enhanced loop”).

        You’ve misunderstood my idea and made it to something much more exotic and interesting. Now, I see where it comes from. My interpretation of the PL/SQL would be

        PROCEDURE proc IS        // void proc()
        BEGIN                    // {
          something1();          //   something1();
          something2();          //   something2();
        EXCEPTION                // catch 
          WHEN OTHERS THEN NULL; //     (Exception e) {}
        END proc;                // }
        
        1. They’re the reason for so many swallowed exceptions. Because of them, we won’t ever have things like CloseableIterable:

          Interesting background info: https://stackoverflow.com/q/34753078/521799. The Java language designers don’t “dare” add support for such features. I guess the time/complexity budget forbids it, probably due to backwards compatibility (or more probably: lack of funds from Oracle).

          My interpretation of the PL/SQL would be

          PROCEDURE proc IS        // void proc()
          BEGIN                    // {
            something1();          //   something1();
            something2();          //   something2();
          EXCEPTION                // catch 
            WHEN OTHERS THEN NULL; //     (Exception e) {}
          END proc;                // }
          

          Hmm, from a consistency (and unrealistic wish-list) perspective, that would not go in the direction I’d love Java to go. Namely, to make the curly braces around methods optional as well. If a method contains a single expression or statement, we should be able to write:

          void proc() something();
          int proc() = something();
          

          Your suggestion would somehow go against this strategy, if that strategy is desireable at all.

          1. Interesting link, indeed, but I don’t think, it really applies to something as trivial as my “once more enhanced loop”. The implementation would be trivial: If the Iterable in the foreach loop is a CloseableIterable, then ensure that its CloseableIterator gets closed, when the loop exits (no matter how).

            So instead of this loop, you’d have

            try (CloseableIterator i = someList.iterator()) {
                for(; i.hasNext(); ) {
                    String item = i.next();
                    System.out.println(item);
                }
            }
            

            I claim, the only problem is that Iterator#next() is not allowed to throw a checked exception, which makes the whole idea of CloseableIterable defunct. Just ditch checked exception and you can safely foreach-iterate over about everything.

            Concerning iterable concatenation et al., it’s just that the existing utilities don’t take closeability into account. Things like

            for (String line : concat(
                file1.lines(Charsets.UTF_8),
                sqlWorker.from(SOMETABLE).select(SOMECOLUMN)) {…}
            

            would work when concat knew that it should close the iterator when done with it (i.e., 1. its hasNext() returns false, 2. the concatenated iterator gets closed, or 3. an exception is thrown by hasNext() or next()).

            1. You’re right, I overlooked that your example doesn’t make use of APIs but of language features, in case of which, closing can be ensured, indeed. (It could be ensured with APIs as well, but setting up reasonable contracts would be much harder).

              Note that Stream.concat() does delegate close() calls to individual streams, so we don’t have a problem there. Closing isn’t done eagerly per substream, though, but lazily per concatenated stream.

  3. what about thinking in what we have instead of something we wish but we won’t able to get it? such syntax can be nice but it’s another wanna-be-cooler feature.

    what about try-monad instead? final Object instance = Try.of(() -> something()).onFailure(throwable -> log.error).then(val ->…).otherwise(() ->…);

    I much prefer it since it isolates the cases and we are free on what to do about it.

    1. Stay tuned. That rant about monads is coming up soon on this blog. Monadomania is just annotatiomania.com, reloaded.

      In this case, one doesn’t exclude the other. I’d really like better language primitives AND better APIs…

      1. well I will read it for sure! :) but don’t get me wrong I was trying to say since there’s no chance to have such feature in Java (well in Java 20 maybe?) I tried to propose something similar.

        but what’s the problem with a try-monad like above anyway?

        1. I didn’t get you wrong, no worries. I’ll explain in that blog post. Otherwise, I’d be writing the whole blog post in a comment :)

    1. Optionals are not intended to be used for exceptions. Something like Try, Future or Either would be more appropriate (look at the Scala API for examples).

  4. Checked exceptions are useless.

    And I do mean useless. If a method declares that it throws a particular exception, what are you supposed to do to handle it? Any ideas? No. Simply knowing what exceptions a method might throw is useless – you need to know when and why a method throws each exception.

    The throws declaration is simply not enough. You still need documentation, which you would provide even if you weren’t using checked exceptions. What’s the point? Where’s the benefit?

  5. Imagine thing this one setp further, and allowing “catch” after the block of a class definition – something like a general exception handler for all the code in the class. Could be useful if there is common exception handling for most/all of the methods in a class.

Leave a Reply