Array or collection of "Autocloseable" in Java8
Asked Answered
D

4

11

Autocloseable should always be used with try-with-resources. At least Intellij inspection suggests it. So, if I have a code that produces Foo that implements Autocloseable I should do:

try (final Foo foo = getFoo()) {
    foo.doSomething();
}

But what if I have function that returns Foo[]? Or function that accepts Foo[] (or Collection<Foo>) as its argument?

How can I use it with try-with-resources? Looks at the following functions:

Foo[] getFoos();
doAll(Foo... foo);

I want to do something line doAll(getFoos())

How can I do that?

Disillusionize answered 7/12, 2016 at 11:56 Comment(0)
E
7

Try-with-resources statement can only close those resources, that were declared and assigned within its header. So the only way is to make the Collection you are getting implement AutoCloseable or wrap it into your AutoCloseable extension, so its close() method will be called by T-W-R. Like:

try (SomeAutoCloseableCollction col = getAutoCloseables()) {
        System.out.println("work");
}  //col.close() gets called

For an array, I'm afraid there is no way, since you can't extend it and make it implement some interface.


If you were to close collection by yourself, may be look at Apache Drill project and class org.apache.drill.common.AutoCloseables - it does exactly that, closing lots of AutoCloseables by itself.

Edie answered 7/12, 2016 at 12:40 Comment(0)
S
6

You can create methods for combining AutoCloseables to a single one which will safely close all of them:

public static AutoCloseable closeBoth(AutoCloseable a, AutoCloseable b) {
    if(a==null) return b;
    if(b==null) return a;
    return () -> { try(AutoCloseable first=a) { b.close(); } };
}
public static AutoCloseable closeAll(AutoCloseable... c) {
    return Arrays.stream(c).reduce(null, MyClass::closeBoth);
}

They allow to use the array returning method like

Foo[] foo;
try(AutoCloseable closeAll = MyClass.closeAll(foo=getFoos())) {
    /*
        use foo
    */
}
Snakebite answered 7/12, 2016 at 13:41 Comment(0)
E
1

As the other answer states this isn't really possible. However you should ask yourself why you would need to put the whole collection in an AutoCloseable. If you want to make sure each element is closed after processing, you can do something like:

Foo[] foos = getFoos();
for (int i = 0; i < foos.length; i++) {
  try (Foo foo = foos[i]) {
    // Your code here
  }
}
Etam answered 7/12, 2016 at 13:2 Comment(4)
I couldn't find a way to make this work with the enhanced for loop but if anyone does see a way please update my answer with an edit.Etam
You mean for(Foo foo: getFoos()) try(Foo toClose=foo) { /* code */ }? That should work.Snakebite
Use case for collection of AutoCloseables: writing to sharded files in an array.Destructive
If your code will throw an exception somewhere in the middle of foos array, the rest of (yet unprocessed) foos will not be closed properly. You need to be accurate to avoid this: e.g. a catch block that never rethrows, or ensuring the rest of foos are closed before rethrowing, or other variants, etc.Polymer
B
0

It's an old question, but I'll share the code I've ended up writing. It's for Closeable rather than AutoCloseable, but you can easily change that.

public class CloseablesCollection<C extends Closeable> extends AbstractCollection<C> implements Closeable {

    private static final Logger log = LoggerFactory.getLogger(CollectionOfCloseables.class);

    private final Collection<C> elements;

    public CloseablesCollection(Collection<C> closeables) {
        this.elements = Collections.unmodifiableCollection(closeables);
    }

    public Collection<C> getElements() {
        return elements;
    }

    @Override
    public void close() throws IOException {
        LinkedList<IOException> exceptions = new LinkedList<>();
        for (Closeable closeable : elements) {
            try {
                closeable.close();
            } catch (IOException e) {
                exceptions.add(e);
            }
        }

        // Throw the last exception. Log the rest.
        while (exceptions.size() > 1) {
            log.warn("Failed close", exceptions.pop());
        }
        if (!exceptions.isEmpty()) {
            throw exceptions.pop();
        }
    }

    @Override
    public Iterator<C> iterator() {
        return elements.iterator();
    }

    @Override
    public int size() {
        return elements.size();
    }
}
Bearwood answered 28/2, 2023 at 15:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.