Computing Thoughts

Java: Evolutionary Dead End

by Bruce Eckel

January 3, 2008



Summary

This sounds bad, but it needs to happen if Java is to ultimately stay in the mainstream. That is, if feature accretion hasn't already irreparably damaged the language.


I'm at Javapolis in Antwerp, Belgium, where I've given a keynote. It's Friday morning, and the day before, Josh Bloch gave a presentation comparing the issues in closures proposals. He sits across from me at breakfast and we further discuss the topic.

Since the beginning I've complained that, at the same time that it claims to be simple, Java is too noisy as a language. Code is read much more than it is written, and this noise directly translates to real costs in software development. Brain cycles are a very scarce resource, and anything that uses them up without benefit -- even something as seemingly innocuous as the extra verbiage in System.out.println() -- takes those cycles away from somewhere they could be useful, and reduces the programming efficiency of the language (Steve Yegge recently wrote about this problem).

In his presentation, Josh said that the last-minute addition of wildcards to Java generics may have pushed the complexity of the language too far. Neal Gafter has suggested that we reify generics. Both were originally unequivocal fans of Java generics, based on their responses to the criticisms I wrote about the topic. Now there seems to be a shift, and I've also seen other people begin to say "generics are still great but..." (Although Tim Bray recently called them a disaster).

The only control we have over complexity is abstraction: hide the parts that don't matter ("divide and conquer" is a variation). The paradox of Java is that a critical aspect of the complexity problem was ignored; code readability was not considered an important issue. It seems that if the IDE writes the code for you, then it doesn't matter if that code is needlessly complex.

Josh took this idea of complexity one step further. He said that it's not just the complexity of a particular feature in isolation, where it can often seem fairly straightforward. It's the combinatorial complexity that you get when you combine a new feature in every possible way with the other language features. When you shoehorn a feature into an existing language rather than carefully designing it in from the beginning, you cannot control how that feature combines with other existing features. The combinatorial complexity can produce horrifying surprises, typically after the feature is added when it's too late to do anything about it. Over breakfast Josh noted that this kind of complexity makes for a bountiful supply of Java puzzlers; entertaining for him but not good for the community as a whole.

Stability vs. the Feature Junkies My epiphany from the impromptu breakfast with Josh was that I am a feature junkie. Features are fun puzzles that, once you figure them out, can be used in fascinating ways. So I've always thought of language evolution in terms of new features. You may discover that you're a feature junkie too. So when features like Java Generics are added badly (in my opinion) to the language, I find it frustrating and my perspective is that they should have just done the right thing when adding the feature. But it never really occurred to me that maybe the right thing to do is just not add the feature at all (what fun is that?). That if you can't do it right then maybe the language should stop growing and become stable. That it should stop chasing every language feature du jour. Arguably one of the best features of C is that it hasn't changed at all for decades. C++ has also been very stable. In that context it doesn't sound so bad that Java stabilize. This isn't to say that features like generics and closures are "bad." Not at all. When well-designed into a language, they can be clear and powerful. But Java had that chance, back at the beginning: Bill Joy made a strong argument to include things like closures and generics before the initial release of the language, but was ignored. People lived tolerably for many years, then suddenly it became essential that generics be shoehorned into the language. This was remarkably coincidental with the appearance of generics in C#, which also appeared to produce several other features in Java 5. It seems that the urgency of these features came not from solving true problems in the Java language, but in Sun trying to maintain the perception of competitiveness against Microsoft's C#. This is probably not so far off the mark, because the reason that Java had to be rushed out in rough form in the first place was the belief that there was a market window that must be captured. A programming language designed by following marketing impulses is eventually going to end up chasing its tail.

The Compatibility Grail One choice would have been to insert the feature correctly and to break backwards compatibility. As a feature junkie that would be my choice because it wouldn't degrade the integrity of the language. This approach was consistently denied because backwards compatibility has always been one of the war axes of the language. I observe that Python has broken backwards compatibility in a small way in an earlier version, amidst much trepidation. The change happened with virtually no backlash, and as a result Python 3 is planning more significant backwards incompatibilities. Ruby is planning on removing many of its Perlish features in order to clean up the language. People who don't want to deal with these changes don't upgrade, and those people tend not to upgrade anyway, out of conservatism. Many companies are still using Java 1.1 for that reason. Those people are not going to be affected one way or another by this debate; they don't plan on changing in any case. If we can't insert features correctly because of backward incompatibility, we are straightjacketed when it comes to language changes; we are in the same position as C++. C++ is often criticized because of its design, but I was on the standards committee for 8 years starting at its inception, and I saw all the debate around each language feature. These were not made capriciously, but very carefully and thoughtfully. What produced the complexity and difficulty of the resulting language was one thing: backwards compatibility with C. Once you bind yourself to backwards compatibility with anything, you must be prepared for your language to be corrupted as you add features. If Java is unwilling to break backwards compatibility, then it is inevitable that it will acquire both unproductive complexity and incomplete implementation of new features. I've made the case in Thinking in Java 4e that Java Generics are only a pale imitation of real generics, and one of the more appealing suggestions for closures (I think it was called "CPA" but I don't have the notes from that Javapolis talk -- maybe someone can give me the correct name) is an incomplete implementation of true closures, but it would actually be preferable to a complete implementation because it produces clearer, more straightforward code. Fundamental new features should be expressed in new languages, carefully designed as part of the ecosystem of a language, rather than being inserted as an afterthought. On my radar, the current best exit strategy for Java is Scala. I have even heard some fairly leading-edge programmers say that at this point they didn't care what happens to Java because they were just going to move on to Scala. If Java is to be saved at all, it needs to become like C; a workhorse that you can rely upon. In fact, any future changes to the language need to be things that simplify and clarify the language and its use (say, fixing the classpath problem), and flesh out (for example) incomplete libraries that have languished (like JMF). But we need to become especially conservative when considering major, fundamental language features like closures which, while they can be very appealing in theory, may have a cost that is too great in practice when they are forced into a language that values backward compatibility over the clarity of its abstractions. (We'll be discussing this and other important Java issues at the upcoming JavaPosse Roundup, March 4-7, 2008, in Crested Butte, CO: http://www.mindviewinc.com/Conferences/JavaPosseRoundup/).

Talk Back!

Have an opinion? Readers have already posted 92 comments about this weblog entry. Why not add yours?

RSS Feed

If you'd like to be notified whenever Bruce Eckel adds a new entry to his weblog, subscribe to his RSS feed.

About the Blogger

Bruce Eckel (www.BruceEckel.com) provides development assistance in Python with user interfaces in Flex. He is the author of Thinking in Java (Prentice-Hall, 1998, 2nd Edition, 2000, 3rd Edition, 2003, 4th Edition, 2005), the Hands-On Java Seminar CD ROM (available on the Web site), Thinking in C++ (PH 1995; 2nd edition 2000, Volume 2 with Chuck Allison, 2003), C++ Inside & Out (Osborne/McGraw-Hill 1993), among others. He's given hundreds of presentations throughout the world, published over 150 articles in numerous magazines, was a founding member of the ANSI/ISO C++ committee and speaks regularly at conferences.

This weblog entry is Copyright © 2008 Bruce Eckel. All rights reserved.