Fix errors when constructing an object from try-with-resources separately from the body

Summary

I have a private type CloseableClass

that can throw an IOError in its constructor, methods, and possibly even internally close

. I want to use try-with-resources and still handle build errors differently with errors during use (usage includes cleanup). Better yet, I would like to write maintainable code.


Suppose you want to construct an instance of a closable class and use it using a try-with-resources statement. It can be called IOException

both in its constructor and in the method used in the body of try-with-resources:

import java.io.Closeable;
import java.io.IOException;
import java.util.Random;

public class CloseableClass implements Closeable {
    public CloseableClass() throws IOException {
        if (new Random().nextBoolean()) {
            throw new IOException();
        }
    }

    public void internetStuff() throws IOException {
        if (new Random().nextBoolean()) {
            throw new IOException();
        }
    }

    public void close() throws IOException {
        if (new Random().nextBoolean()) {
            throw new IOException();
        }
    }

    public static void main(String[] args) {
        try (CloseableClass closeable = new CloseableClass()) {
            closeable.internetStuff();
        }
        catch (IOException e) {
            System.out.println("Bad error!");
        }
    }
}

      

Let's say you want to deal with errors generated in constructor and body separately. Is there a way to do this? In Python, I would do:

try:
    closeable = CloseableClass()
except IOException:
    print("Constructor error")
    return

try:
    with closeable:
        closeable.internet_stuff()
except IOException:
    print("Body error")

      

but in Java, you can't help but assign a second name to an object:

CloseableClass closeable_;

try {
    closeable_ = new CloseableClass();
}
catch (IOException e) {            
    System.out.println("Constructor error!");
    return;
}

try (CloseableClass closeable = closeable_) {
    closeable.internetStuff();
}
catch (IOException e) {
    System.out.println("Body error!");
}

      

I was told that this is "unreachable code" mainly due to usage closeable_

and I disagree. I want to avoid using try-finally, because then you have an even worse emulation problem:

CloseableClass closeable;

try {
    closeable = new CloseableClass();
}
catch (IOException e) {            
    System.out.println("Constructor error!");
    return;
}

try {
    closeable.internetStuff();
}
catch (IOException e) {
    try {
        closeable.close();
    }
    catch (IOException ignore) {
        // Already dealing with this
    }

    System.out.println("Body error!");
}
finally {
    try {
        closeable.close();
    }
    catch (IOException e) {
        System.out.println("Body error!");
    }
}

      

Note that this requires a second call close

, which should be non-working, which the test class doesn't honor (note that it AutoCloseable

doesn't require it, although Closeable

it does). It's a little nicer when close

it can't throw away, but not much.

Basically the problem is that

  • close

    can throw
  • Close before starting IOException

    to prevent printing "Body error!"

    twice
  • Not obvious how to make it work with multiple initializers from try-with-resources
  • You are duplicating code anyway.

Am I just forced to live with "unreachable code" or am I missing a good way to deal with this?

+3


source to share


2 answers


'Note that this requires a second call to close the "no-op" - No, you don't need a block close()

in catch

as the block finally

will always be executed. You will only use close()

internally catch

if you terminate the JVM with a type call System.exit()

in catch

. Typically, you flip Exception

for the caller from the watch catch

, but most of the time you will be doing the cleanup in finally

.

Attempt-resource is better, but you can use type and description Exception

to decipher what went wrong where.

EDIT

In my knowledge, I suggest:



1) Try with a resource:

try(Resource resource = new Resource()){
    // use resource.
}catch(Exception e){
    // handle exception.
    // OR better to throw exception to caller.
    throw e;
}

      

2) Regular style:

Resource resource = null;
try{
    resource = new Resource();
    // use resource
}catch(Exception e){
    // handle exception.
    // OR better to throw exception to caller.
    throw e;
} finally {
   if(resource != null){
       try{
           resource.close();
       } catch(Exception e){
           // most of time you wont or cant do anything here.
       }
   }
}

      

+1


source


One solution is to define a method that packages the initialization errors into a custom exception type and then uses that to determine when the errors occurred.

private CloseableClass createCloseable() throws CloseableCreateException{
    try {
        return new CloseableClass();
    } except (IOException e) {
        throw new CloseableCreateException(e);
    }
}

      

try (CloseableClass closeable = initCloseable()) {
    closeable.internetStuff();
} catch (CloseableCreateException e) {
    System.out.println("Constructor error!");
} catch (IOException e) {
    System.out.println("Body error!");
}

      



Another simple but somewhat tricky solution is to use a boolean flag:

boolean init = true;
try (CloseableClass closeable = new CloseableClass()) {
    init = false;
    closeable.internetStuff();
} catch (IOException e) {
    if (init) {
        System.out.println("Constructor error!");
    } else {
        System.out.println("Body error!");
    }
}

      

0


source







All Articles