Is it beneficial to throw a caught IOException as an UncheckedIOException in order to prevent NullPointerException?

softwareengineering.stackexchange https://softwareengineering.stackexchange.com/questions/380980

  •  15-02-2021
  •  | 
  •  

Question

Not using UncheckedIOException, NullPointerException possible

public void callerMethod() {
    Object result = ioMethod();

    // call instance method of result
}

public Object ioMethod() {
    try {
        // IO Stuff
        return new Object();
    }
    catch (IOException e) {
        e.printStackTrace();
    }

    return null;
}

Using UncheckedIOException, NullPointerException not possible

public void callerMethod() {
    Object result = ioMethod();

    // call instance method of result
}

public Object ioMethod() {
    try {
        // IO Stuff
        return new Object();
    }
    catch (IOException e) {
        throw new UncheckedIOException(e);
    }
}

In both cases, null is not a valid response type of ioMethod (it would only occur due to an IOException). Should I throw the unchecked exception (UncheckedIOException) early in order to prevent an unchecked exception later (NullPointerException) or not try to prevent the NullPointerException?

Was it helpful?

Solution

In both cases, null is not a valid response type of ioMethod (it would only occur due to an IOException).

I beg to differ.

In the first case you have recovered from the exception. What you return now must be a valid response or you are breaking your contract. Returning null is annoying but if it's what you return it's a valid return.

That doesn't mean after recovering you have to return null or avoid returning at all. Your method simply returns either 1 or 0 things. We have something for that.

public void callerMethod() {
    List result = ioMethod(); 

    // call instance method of result
    for (o : result) {
        o.toString(); //No risk of null pointer exception
    }
}

public Object ioMethod() {
    List result = new ArrayList();

    try {
        // IO Stuff
        result.add( new Object() );
    }
    catch (IOException e) {
        e.printStackTrace();
    }

    return result;
}

Collections work really well when you're not sure how many of something you're going to have. If you find that too weird to look at then feel free to learn about Optional. It lets you do pretty much the same thing. The advantage is it makes it more obvious 2 and up won't be involved.

People get weird about 0 and 1. Nothingness is still a hard concept and 1 seems like it has to be special. But that doesn't mean you have to reach for null or avoid returning.

If you insist on null a simple if (result != null) guard takes care of it. It's annoying but that's the deal with null.

You can also return a null object that will quietly do nothing when asked to do something. It's a bit of work to make it but now the caller doesn't have to change. It's hands down my favorite design pattern. If you've ever used the empty string ("") you've already used a form of it.

As for your second case:

The point of throwing exceptions is to either recover by putting the system back into a known good state or to halt the system because you can't get back to a known good state.

Choosing which exception to throw is all about finding the correct recovery code to get back to a known good state.

So leaving it as a NullPointerException is fine, and changing it is fine so long as that ensures the exception is recovered from correctly.

The problem is so damn many things can cause a NullPointerException. Once you allow it to happen (rather than avoid it) it's fairly hard to know what exactly caused it. Which means your try needs to be very small and call very quiet code or you're really just guessing what threw it at you.

So if you are going to rethrow make darn sure you know what you caught. Given that and I'm fine with you avoiding the checked exception nonsense as long as your code base is consistent about it.

OTHER TIPS

TL;DR A method that fails (doesn't fulfill its contract) should make sure that its caller gets an exception, by either creating a new one or by letting some original exception ripple through.

Yes, throwing an UncheckedIOException is beneficial. Not so much for preventing later NullPointerExceptions, but as the preferred way of telling your caller about the ioMethod() failure.

If some methodA() calls ioMethod(), it does so for a reason, and most probably it can't fulfill its job withou success of ioMethod(). So, receiving an exception from ioMethod() automatically makes methodA() also fail with the same exception, without the need to write one specific error-handling code line in methodA().

If you choose to return null, you force methodA() to wrap the ioMethod() call in a conditional, and we all know from the bad old no-exceptions days that programmers are lazy and tend to forget about error handling.

Returning null is a valid solution if it can be viewed as a valid outcome of the method instead of a failure. And then its name should clearly indicate that (e.g. a name like tryToRead(), to remind the "lazy programmer" to deal with the null value.

Licensed under: CC-BY-SA with attribution
scroll top