简体   繁体   中英

Java and objects initialising multiple resources - is there a better way?

In Java, we hit many cases of successfully loading one resource but then failing to load the second resource.

We ended up coming to a pattern for it:

public class MultipleResourceHolder implements AutoCloseable {
    private final AutoCloseable resource1;
    private final AutoCloseable resource2;

    MultipleResourceHolder() {
        try {
            resource1 = // create it
            resource2 = // create it
        } catch (Throwable t) {
            close();
            throw t;
        }
    }

    @Override
    public void close() {
        if (resource1 != null) {
            try {
                resource1.close();
            } catch (Exception e) {
                // handle it
            }
        }
        if (resource2 != null) {
            try {
                resource2.close();
            } catch (Exception e) {
                // handle it
            }
        }
    }
}

There's obviously a lot of boilerplate here though. It gets slightly uglier again if your class also extends some other class which holds its own resources. Also, having the object close itself feels wrong somehow.

Is there a more elegant way to do this? I have tried a CloseableList type of approach where you add the objects to the list and it certainly tidies up close() a bit, but the constructor itself doesn't get any better and it adds a field to the class just to do cleanup. :/

There isn't any particularly nice way. The try-with-resources pattern takes care of a huge amount of boilerplate (which people would usually get wrong in its absence) in cases where it works, but it isn't always useful.

Note also that there is no decent way for a chained constructor call to safely create resources without risk of leakage if the constructor throws, nor can the constructor of an inheritable class create resources in a fashion which will be robust if a derived-class constructor throws. I would suggest that contrary to the normal advice about calling virtual methods within a base constructor, the only safe way to have a base constructor create resources is to require derived classes to do all their real setup work in a method called from the base constructor, such that the derived-class constructor doesn't do any work and thus can't throw. That unfortunately means derived classes can't declare their fields as final , but I don't know any good workaround for that.

Upon some further consideration, it may be possible to do things reasonably well if the base class defines a Parameters class which includes a success flag and a (possibly do-nothing) Close method; each derived class should derive a parameter-holding class from that of its parents, including whatever additional parameters it needs. Each class should have a protected constructor which accepts its own derived parameters type, and include a public static factory method which will construct a suitable "parameter" object and pass it to the constructor within a try-with-resources block something like:

FancyClass create(params)
{
  FancyClass result;
  try (FancyClassParams params = new FancyClassParams(params))
  {
    FancyClassParams.init();
    result = new FancyClass(FancyClassParams);
    result.init(FancyClassParams);
    FancyClassParams.success = true;
  }
}

The constructor for FancyClassParams should acquire no resources, and be simple enough that it can never fail; if it's necessary to open any files or otherwise acquire resources before calling the main constructor, that should be done in init . The close method for FancyClassParams should check whether success is set and, if not, clean up any resources that it had acquired.

The above approach would require a fair bit of code in each derived class to ensure cleanup always gets performed as it should, but should robustly clean resources even if exceptions occur at any permissible point in the construction process.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM