20 comments

[ 2.8 ms ] story [ 42.6 ms ] thread
It's a much needed cleanup of something that's always resulted in clutter in my code. C# has had this feature for over a decade. Nice to see Java adding this since I have a lot of Java code to maintain. Like a lot of Java devs we're switching over to Scala for new projects and using vanilla Java for legacy code. Automatic resource management can be implemented trivially in Scala without the need for a new language feature to support it. Structural subtyping is awesome!
How does structural subtyping help you implement automatic resource management?
I can define a resource management method which accepts any object which belongs to a class that provides a close method irrespective of whether that class implements close as part of an an interface or not.
Rough example below, partially taken from a book I have on Scala.

//define it like this: def using[T <: {def close(): Unit }, S](obj: T) (operation: T => S) = { val result = operation(obj) obj.close() result }

//use it like this: val writer = new FunkyWriter using(writer) { writer.write("outputting some data") }

ARM was therefore possible in Scala before the Closable interface was even added to the Java libs.

(I had another comment here but I figured out the answer.)

lombok's @Cleanup will close anything, interface or not.

I took a look at lombok and it's pretty obvious from the way cleanup method names can be passed as strings that's it's using reflection. It's a bit of a nasty hack, but it works.
It fiddles with the AST during compilation. No reflection.
Sounds like it's trying to add structural subtyping via the back door.
(comment deleted)
Great, now it just needs a way to prevent you from forgetting to use that...
Offtopic: In the past ~week Blogger blogs have started opening on the comments section rather than the top of the page. Anyone else?
(comment deleted)
its nice but the issue for me is that it will be backwards compatible so the preJRE7 way of managing IO resources is also included , this means an even more bloated java api :-(
I think the only thing that was added to the API was a single interface called Closeable. Adding 1 interface to a library is hardly bloat. The change is essentially language support for a common syntax for resource management, not a new API.
There's a problem with box 2. If the IOException is thrown in the stream's constructor (FileNotFoundException) then there will be a NullPointerException if we try to call close!

  InputStream in = null;
  try
  {
      in = new FileInputStream(new File("test.txt"));
      //do stuff with in
  }
  catch(IOException ie)
  {
      //SOPs
  }
  finally
  {
      try
      {
          if(in != null)
          {
              in.close();
          }
      }
      catch(IOException ioe)
      {
          //can't do anything about it
      }
  }
This merely proves your point! :-)
Thanks, I didn't realize it's this bad. My only touch base with Java has been through Clojure.

This is just horrible. What braindead system forces you to check exceptions on closing a file handle? If everything failed silently and just produced 'null', the code would look like:

  InputStream in = new FileInputStream(new File("test.txt"));
  if (in) {
    ...dostuff...
    in.close();
  }
Now, that you can grasp with one glance. I'll trade the ability to write something like that with having to use malloc()/free() any day.
Silent failure doesn't carry information about how the thing failed: did the file not exist, was it not accessible because of security, was there a failure on the underlying device or file system?

How is the user supposed to figure out what went wrong with the program if the program can't even convey to the user what went wrong?

Complex problems have lots of simple wrong answers.

(Your "solution" also suggests nothing about how "in.close()" is going to get called if "...dostuff..." itself throws an exception.)

This is a welcome addition to java but I wish they had chosen a slightly different syntax. My main gripe is that its not clear what the lexical scoping rules are. Most likely the variable declared in the try is only available in the body of the try, not in the catch but its an extra thing to know. As opposed to

   InputStream in;
   try(in = new FileInputStream()){
      ...
   } catch (...){
      ...
   }
Now there are no new lexical scoping rules to understand. A lesser point is that I find the try(x = e) syntax ugly. It reminds me of the C-ism where a variable is set and tested in an if statement.

   if (x = getline()){
     ...
   }
Control flow and side effects should not be mixed, IMO.
If Java had stack allocated objects there would be no need for new interfaces nor new syntax.
It's not that simple. It doesn't just apply to stack allocated objects; resource ownership (in the sense of being responsible for "closing" the resource when you are yourself "closed") may and often does logically form a tree throughout the heap, and doesn't easily flatten to stack allocated objects without going further into allocating objects inline in other objects.

And when you introduce that, you have a problem of dealing with the difference between references to these inline objects: possibly on the stack or the heap, free-standing or embedded. If a reference escapes and outlives the lifetime of the object, you've created a potential dangling pointer, whereas in the normal Java or C# implementation you have at worst a semantic problem of having leaked a reference to a "closed" or disposed object. The GC won't collect it, but any operations on it will and should fail.

If you try and deal with this in the C++ way, you'll end up with far more confusion over objects passed around by reference, and objects passed around by value. Instead of the mechanism of passing being encoded by the type, it would be part of the control flow, and it would be easy to get into situations where you're working with a reference to a copy that was passed by value, and think you have a reference to the original; this is already a problem in C# to the degree that it is not recommended to create mutable value types.

TL;DR: what you suggest would create far more problems than it solves.