Hacker News new | ask | show | jobs
by bArray 2379 days ago
> You are right, but you still have to catch the checked

> IOException.

Rather than which alternative? There are tonnes of IO errors that can occur and are captured in Java that you may not ever even consider. IO is exceptionally tough and programmers should be aware that there could be 1 of a possible million reasons for failure, even if they don't care exactly why.

It's not a massive ask either:

    try{
      /* Perform some IO that 99.999999% of the time */
    }catch(Exception e){
      /* Something went wrong and we don't know the state of the disk */
    }
Personally I believe applications should have their own wrapper around IO and handle it accordingly. I.e. not caring, re-trying, show-stopper, etc, etc.

A lot of code I've written will have the following if I really don't care if it works or not:

    try{
      /* IO code */
    }catch(Exception e){
      /* Do nothing */  // <-- Let others know that this was on purpose
    }
2 comments

99% of the time I want to do the same thing for IOException as I would for NoSuchElementException or a DOMException.

That said, even if typed checked exception handling is important...it quickly becomes untenable.

   Thing thing = create();
   try {
     ...
   } finally {
     cleanup(thing);
   }
You might try to abstract this

   withThing(thing -> ...)
But what if "..." potentially throws IOException? What if it potentially throws IOException or AWTException?

You have to give up all your exception type safety to make an abstraction like withThing. It's just not composable.

    interface ThrowingConsumer<T, E extends Exception> {
        void accept(T value) throws E;
    }

    // ...

    <E extends Exception> void withThing(ThrowingConsumer<Thing,E> callback) throws E { ... }
The problems are that (a) you need to add an extra type parameter for exceptions all over the place, (b) exception unification (sum type) doesn't work generically - you can say E1 | E2 in a catch block but it's not a real type, and (c) it composes poorly with existing libraries that expect Consumer<T> throughout.

For one level deep callbacks (for resource handling and the like) it works reasonably well though.

(b) is what I had in mind as the problem.
You can always opt out of the typed checked exceptions.

    try {
        ...
    } except (IOException|NoSuchElementException e) {
        throw new RuntimeException(e);
    }
If you decide you don't want checked exceptions in your code, wrapping the exceptions at the boundaries is not a huge deal.
Or switch to a language that does that for you, like Scala or Kotlin.
For a lot of scripts you want the program to crash with a stack trace if you get an IOException, which is the default behavior if it's uncaught. The user is the developer, and if there's an error the developer fixes it by munging something on their filesystem.

Java was designed for "production" software, which in the late 90s and early 00s meant software that ran for long periods of time as a server, servicing thousands of mission-critical customers. Crashing was not acceptable behavior for that. But now a lot of production software often requires a lot of ancillary one-off tasks that are just done by the developers - testing, data-munging, exploratory code, migrations, demos, etc. That's a very different environment from where you code to spec, the spec never changes, and once the software is done it's supposed to run for years without crashing.

Early Java was designed for set-top boxes and then web browsers. Large-scale servers came later; in fact a lot of libraries still don't support the async futures that were officially added five years ago.