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:
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?