7

I have a class which implements AutoCloseable, and is intended to be used with Java 7's new try-with-resources construct. However, I can't figure out a way to guarantee that the user of my class uses try-with-resources. If this doesn't happen, then my class won't be able to close itself, and bad things will happen. Is there any way - language construct or otherwise - to enforce this? Even being able to detect whether or not I'm in a try-with-resources block so that I can throw an exception if not would be good (although a compile-time construct would be preferable).

Thanks!

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
joshlf
  • 21,822
  • 11
  • 69
  • 96
  • What is the user wanted to manage `close`ing your class manually instead of automagically with a try-with-resources? Would creating a `finalize` method that called `close` fix your problem? – Jeffrey Jul 02 '13 at 16:12
  • 1
    Unfortunately, `finalize` is not guaranteed to be called. The GC may decide not to call it. – joshlf Jul 02 '13 at 16:59
  • In fact, I imagine that the unreliability of `finalize` is probably the reason that try-with-resources was introduced. – joshlf Jul 02 '13 at 16:59
  • It's better than nothing, and there is no guarantee that your user *has* to call `close` on your class before it gets GCed. – Jeffrey Jul 02 '13 at 17:01
  • 1
    That's the whole point - I'm trying to figure out how to implement such a guarantee. If I can force them to use try-with-resources, then I get that guarantee. – joshlf Jul 02 '13 at 17:05

3 Answers3

4

Unfortunately there's no way to protect yourself from user stupidity.

You can implement the finalize method to call close; this way you can be sure that the resource is closed at least when the object is garbage collected, even though you can't know when (or if) that happens.

If you can put restrictions on how your project is used, you may be able to enforce some policies using aspect oriented programming, but then you're not really using Java anymore.

Joni
  • 108,737
  • 14
  • 143
  • 193
  • 1
    The use of Object.finalize() is highly discouraged in java. – David Hofmann Jul 02 '13 at 16:15
  • Unless you know what you are doing *and* have no other option ;) – Joni Jul 02 '13 at 16:16
  • 2
    @DavidHofmann: I think you have misunderstood something. It's probably (and with reason) discouraged to rely on finalize being invoked by the GC, but actually *implementing* the finalize method is in some cases the only option to prevent resource leakage. – jarnbjo Jul 02 '13 at 16:20
  • 2
    I agree @jarnbjo, I would however let the client of my api die fast and keep them informed about the missuse of the api, than allowing them to have non-deterministic behaviour. – David Hofmann Jul 02 '13 at 17:23
1

If you really care about resource management, then using a callback idiom is the safest approach. You don't expose the collection, but an API that allows the end-user to handle items in the collection:

public interface Callback<T> {
  void handle(T item);
}

public class SuperVitalVault {
  public void all(Callback<Precious> callback) {
    try (...) {
      for (Precious i : ...) {
        callback.handle(i);
      }
    }
  }
}

You can change Callback.handle(T) to return a boolean if you want to support early exit:

    try (...) {
      for (Precious i : ...) {
        if (callback.handle(i)) break;
      }
    }

If you want to stay away from defining your own callback interface, you can use Guava's Function<T, Boolean> or Predicate<T>, but note that it violates the semantics established by Guava:

Instances of Function are generally expected to be referentially transparent -- no side effects -- and to be consistent with equals, that is, a.equals(b) implies that function.apply(a).equals(function.apply(b)).

Instances of Predicate are generally expected to be side-effect-free and consistent with equals.

Dilum Ranatunga
  • 13,254
  • 3
  • 41
  • 52
-1

No, there is no way, Same bad things will happen if you keep opening files or database connections and you don't close them...

If the sensible resources can be used and discarted within the scope of one method call, you can open/close every time your api is called, if not, then just have well documented the behaviour.

Your class might also register a shutdown hook so you can at least close your resources when the jvm is going down, but I see this as a bad practice for a library anyway.

David Hofmann
  • 5,683
  • 12
  • 50
  • 78
  • 1
    You're wrong. Both files and network or database connections are closed when finalizing the respective wrapper instances as a last possibility to prevent resource leakage. There's no good reason not to use the same implementation pattern for own purposes. – jarnbjo Jul 02 '13 at 16:22
  • Thanks @jarnbjo for clarification. What I had in mind though is that if you keep opening resources without closing them, you have a leak. I think it is anyway better to see the leak sooner than later, in the case that you somehow keep references to the unclosed objects then no finalize meethod will be called. If you rely on GC, you might have non-deterministic behaviour depending on the amount of ram and gc tunning that runs your program – David Hofmann Jul 02 '13 at 17:15