Tuesday, February 26, 2008

I'm Tired of Closing Files

Not long ago, color commentator Bruce Eckel shocked the Java world with his article entitled: "Java: Evolutionary Dead End".

The press went wild stating that Java was doomed since one of its biggest supporters was now shunning it. If you actually read the article, this doesn't call for the end of Java, just slowing down or completely stopping the adoption of new features to the language akin to what C and C++ are today. C and C++ are governed by slow ISO committees that make a new language specification every ten years or so. Instead, he argues, hit the reset button and make a new language on top of the JVM. Certainly, the JVM is no longer a stranger to non-Java languages, both Groovy and JRuby seem to be carving a niche for themselves.

Furthermore, the Java virtual machine, at least the one from Sun, is constantly improving both in terms of speed and memory consumption. The version 6 virtual machine of HotSpot from Sun is nothing short of spectacular. New language, same great infrastructure, sounds perfectly reasonable doesn't it?

Now, why would he choose such a tittle? I'm familiar enough with this author, I probably still have the 1st edition of Thinking in C++ somewhere, to know that it was probably on purpose. He knew the
attention he would get with this kind of title than with one that more accurately portrayed the contents of his article. Nonetheless, it made a lot of people think about the core issue, should Java be frozen or not?

I've been thinking about that lately and I've come to the conclusion that I'm tired of closing files or any other type of resources for that matter. I think it's reasonable to freeze Java (the language)
but only after it has indeed reached adulthood. You can talk about Java 7's closures, asynchronous I/O and super packages all you want but it doesn't change the fact that I'm still sick of closing files.

With a garbage collector present, it is no longer clear when the destructor/finalizer of a class will execute. If I were to write a program that operates on files, I would have to manually close any file object created or risk running out of file descriptors depending on the operating system I am running on. The same can be said for any object that holds kernel level resources. Coming from C++, this is something I no longer needed to care about, why should I care about it in Java?

C# does something interesting here, any object that implements the IDisposable interface can be used inside a "using" statement. This means that at the end of the "using" block, the dispose method is automatically called. Something like this:

using System;
using System.IO;

class Program {
static void Main(string[] args) {
using (Stream stream = File.Open("foo", FileMode.Open)) {
// Operate on stream to foo
}
}
}
The problem I have with that is that it's still bound to one variable per "using" block. So, to copy a file to another, I need at least two "using" blocks.

In C++, the resource initialization is acquisition idiom is increasingly popular, made even more popular by the use of smart pointers, so assuming the use of shared_ptr here:
if (shared_ptr<x> p = someManager.get(id)) {
// operate on p
} else {
// p is also not in scope here
}
// p is no longer visible

All this to say, that the equivalent construct is missing in Java. Not only, but I don't like the C# solution as it's possible to go further.

If you have multiple "using" (or "if") blocks, why can't we just make a list? In other words, if I can acquire this list of resources, then execute the block otherwise don't? For example:
// Fake Java code sample
using (FileChannel r = new FileInputStream(path).getChannel(),
WritableByteChannel w =
Channels.newChannel(response.getOutputStream())) {

r.transferTo(0, length, w);
} finally {
// handle failure
}
Instead, I have to do something like:
// might throw
FileChannel fc = new FileInputStream(file).getChannel();
WritableByteChannel w =
Channels.newChannel(response.getOutputStream());

if (w != null)
fc.transferTo(0, file.length(), w);

// null or not, w is still in scope
// Don't really have to close, but waiting for GC might be fatal later on
fc.close();
I write a lot of Java code now and I'm really enjoying it, I wouldn't go back to C++ at this point. C++ is only the last resort language now, the language that makes things work if it doesn't anywhere else. So, when I hear things about freezing Java, I think about things like this. I wonder how much time Mr. Eckel spends writing code versus teaching it. As an educator, I can understand the importance of a non-moving target, but in the field, I need relief for some common idioms. Saying let's do a new language won't make all the tooling that's available today for Java suddenly appear on that language. For example, I really like Groovy but I don't use it. The IDE support in NetBeans just isn't good enough for me to work with it.

No comments: