Can Java Be Saved?

| | Comments (8) | TrackBacks (0)

Java and Evolution

The Java language has been around for a pretty long time, and in my view is now a stagnant language. I don't consider it dead because I believe it will be around for probably decades if not longer. But it appears to have reached its evolutionary peak, and it doesn't look it's going to be evolved any further. This is not due to problems inherent in the language itself. Instead it seems the problem lies with Java's stewards (Sun and the JCP) and their unwillingness to evolve the language to keep it current and modern, and more importantly the goal to keep backward compatibility at all costs. Not just Sun, but also it seems the large corporations with correspondingly large investments in Java like IBM and Oracle aren't exactly chomping at the bit to improve Java. I don't even know if they think it even needs improvement at all. So really, the ultra-conservative attitude towards change and evolution is the problem with Java from my admittedly limited view of things.

That's why I don't hate Java. But, I do hate the way it has been treated by the people charged with improving it. It is clear many in the Java community want things like closures and a native property syntax but instead we got Project Coin. This, to me, is sad really. It is a shame that things like closures and native properties were not addressed in Java/JDK/whatever-it-is-called 7.

Why Not?

I want to know why Java can't be improved. We have concrete examples that it is possible to change a major language in major ways. Even in ways that break backward compatibility in order to evolve and improve. Out with the old, in with the new. Microsoft with C# showed that you can successfully evolve a language over time in major ways. For example C# has always had a property syntax but it now also has many features found in dynamically typed and functional languages such as type inference and, effectively, closures. With LINQ it introduced functional concepts. When C# added generics they did it correctly and retained the type information in the compiled IL, whereas Java used type-erasure and simply dropped the types from the compiled bytecode. There is a great irony here: though C# began life about five or six years after Java, it not only has caught up but has surpassed Java in most if not all ways, and has continued to evolve while Java has become stagnant.

C# is not the only example. Python 3 is a major overhaul of the Python language, and it introduced breaking changes that are not backwards compatible. I believe they provide a migration tool to assist you should you want to move from the 2.x series to version 3 and beyond. Microsoft has done this kind of thing as well. I remember when they made Visual Basic conform to the .NET platform and introduced some rather gut wrenching (for VB developers anyway) changes, and they also provided a tool to aid the transition. One more recent example is Objective-C which has experienced a resurgence in importance mainly because of the iPhone. Objective-C has been around longer than all of Java, C#, Ruby, Python, etc. since the 1980s. Apple has made improvements to Objective-C and it now sports a way to define and synthesize properties and most recently added blocks (effectively closures). If a language that pre-dates Java (Python also pre-dates Java by the way) can evolve, I just don't get why Java can't.

While it is certainly possible to remain on older versions of software, forcing yourself to upgrade can be a Good Thing, because it ensures you don't get the "COBOL Syndrome" where you end up with nothing but binaries that have to run on a specific hardware platform forever and you are trapped until you rewrite or you go out of business. The other side of this, of course, is that organizations don't have infinite time, money, and resources to update every single application. Sometimes this too can be good, because it forces you to triage older systems, and possibly consolidate or outright eliminate them if they have outlived their usefulness. In order to facilitate large transitions, I believe it is very important to use tools that help automate the upgrade process, e.g. tools that analyze code and fix it if possible (reporting all changes in a log) and which provide warnings and guidance when a simple fix isn't possible.

The JVM Platform

Before I get into the changes I'd make to Java to make it not feel like I'm developing with a straightjacket on while having to type masses of unnecessary boilerplate code, I want to say that I think the JVM is a great place to be. Obviously the JVM itself facilitates developing all kinds of languages as evidenced by the huge number of languages that run on the JVM. The most popular ones and most interesting ones these days are probably JRuby, Scala, Groovy, and Clojure though there are probably hundreds more. So I suppose you could make an argument that Java doesn't need to evolve any more because we can simply use a more modern language that runs on the JVM.

The main problem I have with that argument is simply that there is already a ton of Java code out there, and there are many organizations who are simply not going to allow other JVM-based languages; they're going to stick with Java for the long haul, right or wrong. This means there is a good chance that even if you can manage convince someone to try writing that shiny new web app using Scala and its Lift framework, JRuby on Rails, Grails, or Clojure, chances are at some point you'll also need to maintain or enhance existing large Java codebases. Wouldn't you like to be able to first upgrade to a version of Java that has closures, native property syntax, method/property handles, etc?

Next I'll choose what would be my top three choices to make Java much better immediately.

Top Three Java Improvements

If given the chance to change just three things about Java to make it better, I would choose these:

  • Remove checked exceptions
  • Add closures
  • Add formal property support

I think these three changes along would make coding in Java much, much better. Let's see how.

Remove Checked Exceptions

By removing checked exceptions you eliminate a ton of boilerplate try/catch clauses that do nothing except log a message, wrap and re-throw as a RuntimeException, pollute the API with throws clauses all over the place, or worst of all empty catch blocks that can cause very subtle and evil bugs. With unchecked exceptions, developers still have the option to catch exceptions that they can actually handle. It would be interesting to see how many times in a typical Java codebase people actually handle exceptions and do something at the point of exception, or whether they simply punt it away for the caller to handle, who in turn also punts, and so forth all the way up the call stack until some global handler catches it or the program crashes. If I were a betting man, I'd bet a lot of money that for most applications, developers punt the vast majority of the time. So why force people to handle something they cannot possible handle?

Add Closures

I specifically listed removing checked exceptions first, because to me it is the first step to being able to have a closure/block syntax that isn't totally horrendous. If you remove checked exceptions, then adding closures would seem to be much easier since you don't need to worry at all about what exceptions could possibly be thrown and there is obviously no need to declare exceptions. Closures/blocks would lead to better ability to handle collections, for example as in Groovy but in Java you would still have types (note I'm also using a literal property syntax here):

// Find all people whose last name is "Smith"
List<Person> peeps = people.findAll { Person person -> person.lastName.equals("Smith");   } 
or
// Create a list of names by projecting the name property of a bunch of Person objects
List<String> names = people.collect { Person person -> person.name; }

Not quite as clean as Groovy but still much better than the for loops that would traditionally be required (or trying to shoehorn functional-style into Java using the Jakarta Commons Collections or Google Collections). Removal of checked exceptions would allow, as mentioned earlier, the block syntax to not have to deal with declaring exceptions all over the place. Having to declare checked exceptions in blocks makes the syntax worse instead of better, at least when I saw the various closure proposals for Java/JDK/whatever 7 which did not get included. Requiring types in the blocks is still annoying, especially once you get used to Ruby and Groovy, but it would be passable.

Native Property Syntax

The third change should do essentially what Groovy for properties does but should introduce a "property" keyword (i.e. don't rely on whether someone accidentally put an access modifier in there as Groovy does). The syntax could be very clean:

property String firstName;
property String lastName;
property Date dateOfBirth;

The compiler could automatically generate the appropriate getter/setter for you like Groovy does. This obviates the need to manually code the getter/setter. Like Groovy you should be able to override either or both. It de-clutters code enormously and removes a ton of lines of silly getter/setter code (plus JavaDocs if you are actually still writing them for every get/set method). Then you could reference properties as you would expect: person.name is the "getter" and person.name = "Fred" is the "setter." Much cleaner syntax, way less boilerplate code. By the way, if someone used the word "property" in their code, i.e. as a variable name, it is just not that difficult to rename refactor, especially with all the advanced IDEs in the Java community that do this kind of thing in their sleep.

Lots of other things could certainly be done, but if just these three were done I think Java would be much better off, and maybe it would even come into the 21st century like Objective-C. (See the very long but very good Ars Technica Snow Leopard review for information on Objective-C's new blocks feature.)

Dessert Improvements

If (as I suspect they certainly will :-) ) Sun/Oracle/whoever takes my suggestions and makes these changes and improves Java, then I'm sure they'll want to add in a few more for dessert. After the main course which removes checked exceptions, adds closures, and adds native property support, dessert includes the following:

  • Remove type-erasure and clean up generics
  • Add property/method handles
  • String interpolation
  • Type inference
  • Remove "new" keyword

Clean Up Generics

Generics should simply not remove type information when compiled. If you're going to have generics in the first place, do it correctly and stop worrying about backward compatibility. Keep type information in the bytecode, allow reflection on it, and allow me to instantiate a "new T()" where T is some type passed into a factory method, for example. I think an improved generics implementation could basically copy the way C# does it and be done.

Property/Method Handles

Property/method handles would allow you to reference a property or method directly. They would make code that now must use strings strongly typed and refactoring-safe (IDEs like IntelliJ already know how to search in text and strings but can never be perfect) much nicer. For example, a particular pet peeve of mine and I'm sure a lot of other developers is writing Criteria queries in Hibernate. You are forced to reference properties as simple strings. If the lastName property is changed to surname then you better make sure to catch all the places the String "lastName" is referenced. So you could replace code like this:

session.createCriteria(Person.class)
	.add(Restrictions.eq("lastName", "Smith")
	.addOrder(Order.asc("firstName")
	.list();

with this using method/property handles:

session.createCriteria(Person.class)
	.add(Restrictions.eq(Person.lastName, "Smith")
	.addOrder(Order.asc(Person.firstName)
	.list();

Now the code is strongly-typed and refactoring-safe. JPA 2.0 tries mightily to overcome having strings in the new criteria query API with its metamodel. But I find it pretty much appalling to even look at, what with having to create or code-generate a separate "metamodel" class which you reference like "_Person.lastName" or some similar awful way. This metamodel class lives only to represent properties on your real model object for the sole purpose of making JPA 2.0 criteria queries strongly typed. It just isn't worth it and is total overkill. In fact, it reminds me of the bad-old days of rampant over-engineering in Java (which apparently is still alive and well in many circles but I try to avoid it as best I can). The right thing is to fix the language, not to invent something that adds yet more boilerplate and more complexity to an already overcomplicated ecosystem.

Method handles could also be used to make calling methods using reflection much cleaner than it currently is, among other things. Similarly it would make accessing properties via reflection easier and cleaner. And with only unchecked exceptions you would not need to catch the four or five kinds of exceptions reflective code can throw.

String Interpolation

String interpolation is like the sorbet that you get at fancy restaurants to cleanse your palate. This would seem to be a no-brainer to add. You could make code like:

log.error("The object of type  ["
    + foo.getClass().getName()
    + "] and identifier ["
    + foo.getId()
    + "] does not exist.", cause);

turn into this much more palatable version (using the native property syntax I mentioned earlier):

log.error("The object of type [${foo.class.name}] and identifier [${foo.id}] does not exist.", cause);

Type Inference

I'd also suggest adding type inference, if only for local variables like C# does. Why do we have to repeat ourselves? Instead of writing:

Person person = new Person();

why can't we just write:

var person = new Person();

I have to believe the compiler and all the tools are smart enough to infer the type from the "new Person()". Especially since other strongly-typed JVM languages like Scala do exactly this kind of thing.

Elminate "new"

Last but not least, and actually not the last thing I can think of but definitely the last I'm writing about here, let's get rid of the "new" keyword and either go with Ruby's new method or Python's constructor syntax, like so:

// Ruby-like new method
var person = Person.new()

// or Python-like construction
var person = Person()

This one came to me recently after hearing Bruce Eckel give an excellent talk on language evolution and archaeology. He had a ton of really interesting examples of why things are they way they are, and how Java and other languages like C++ evolved from C. One example was the reason for "new" in Java. In C++ you can allocate objects on the stack or the heap, so there is a stack-based constructor syntax that does not use "new" while the heap-based constructor syntax uses the "new" operator. Even though Java only has heap-based object allocation, it retained the "new" keyword which is not only boilerplate code but also makes the entire process of object construction pretty much immutable: you cannot change anything about it nor can you easily add hooks into the object creation process.

I am not an expert at all in the low-level details, and Bruce obviously knows what he is talking about way more than I do, but I can say that I believe the Ruby and Python syntaxes are not only nicer but more internally consistent, especially in the Ruby case because there is no special magic or sauce going on. In Ruby, new is just a method, on a class, just like everything else.

Conclusion to this Way Too Long Blog Entry

I did not actually set out to write a blog whose length is worthy of a Ted Neward blog. It just turned out that way. (And I do in fact like reading Ted's long blogs!) Plus, I found out that speculative fiction can be pretty fun to write, since I don't think pretty much any of these things are going to make it into Java anytime soon, if ever, and I'm sure there are lots of people in the Java world who hate things like Ruby won't agree anyway.

8 Comments

Andy said:

Indeed, well said, friend -- I was a bit leary about closures in Java (they're already available in Groovy, so why add them?) but Ted Neward convinced me otherwise (his point being that if there were a native closure type, then one could "pass a closure from Groovy to Java to Scala" etc).

I would like to point out, however, that while Python did change with 3.0 "and it introduced breaking changes that are not backwards compatible" Guido van Rossum (Python's founder) recently proposed a "moratorium on Python language changes" for a few years -- see http://mail.python.org/pipermail/python-ideas/2009-October/006305.html -- but check out the key reason why:

"The reason is that frequent changes to the language cause pain for implementers of alternate implementations (Jython, IronPython, PyPy, and others probably already in the wings) at little or no benefit to the average user (who won't see the changes for years to come and might not be in a position to upgrade to the latest version for years after)."

The first part is great (yeah, he's waiting for Jython, etc), but check out the last part -- "little or no benefit to the average user" -- perhaps the people behind the Java language feel the same?

brweber2 said:

I would love to see Java the language enhanced as much as the next developer, but here are 12 comments ...

1) Sun simply doesn't have the resources at the moment to make these changes
2) You left of Jython of your list of common alternatives on the JVM, the last I checked it was #2 (after Groovy) when searching on indeed.com for jobs.
3) I think that Project Coin gets a bad wrap. People wanted more, but Project Coin was never intended to be more... it always always meant to be "small change."
4) Maybe keeping Java exactly as is... is ok. Maybe what we really want is a new language that is more like Java than Scala, Groovy, JRuby, Jython or Clojure, but that is NOT Java? It sounds like even more work but it will allow the backwards compatibility for the Java language and allow progression at the same time. The biggest problem is who will pay for the Java.fork?
5) Removing checked exceptions entirely might not be a good idea. As annoying as they are, they do serve the nice purpose of documenting what exceptions can occur when calling a method. For low level things like File IO it is probably still a good thing to have checked exceptions to increase the probability that resources will be cleaned up properly. If the exceptions are not known (unchecked), your only real chance is to catch RuntimeException, Error or Throwable (in order of 'badness' from least to most). A 'using' syntax might remove the usefulness of checked exceptions but you didn't include it on your list!
6) Closures... ah, it would be nice to have them ... but it would be that much better if not only were they added but the standard libraries were updated as well to use all these features...
7) With your proposed property syntax how would one add a read only or write only property? Allowing for fine grained access control is pretty important.
8) Type erase is a tradeoff. There are times when it would be fantastic to have the type information at runtime and times when it can be a hindrance. I believe that Rich Hickey wanted type erasure for Clojure (but I could be wrong about that).
9) Method handles are being added to Java 7, but they are low level and probably not what you have in mind. And they probably won't be used in Java the language, at least for a while (*I think*).
10) Why not add multi-line strings in addition to string interpolation? The string interpolation in JavaFX isn't bad.... I'd suggest that syntax for interpolation.
11) Type inference of the type you described would be fine in cases when you didn't want less specific compile time information about the type of the variable. Also, this is the second new keyword you have proposed! :)
12) The parser can't allow the removal of the new keyword in the way you have described. There is no way to differentiate a method invocation vs. class instantiation if you were to remove the 'new' keyword in the Java language. You would have to revert to some nasty rule such as looking for a constructor first and if you don't find one, then assume it is a method... or make a new rule that you could not have a method with the same name as the class, a rule that does not exist in Java today.

What do you think about allowing multiple Exception types or'd together with a single block to handle them all? This has been proposed in the past and seems like it also would remove some boilerplate as well.

I would love to see primitives go away too! Many people will claim that they are necessary for performance, but if ONLY the objects were present then it would be up to the JVM implementors to find ways to increase performance over time and I believe that they could/would do this using primitives under the covers when possible.

Scott Leberknight Author Profile Page said:

1. Agree that Sun probably doesn't have the resources

2. Just forgot about Jython. Didn't realize it was #2 JVM language

3. My point about coin was simply that we got small change instead of change that makes a significant difference

4. It might indeed be OK to leave Java as-is. And we do have new languages that I'd say are more flexible, powerful, "better", whatever. What Stu Halloway refers to as Java.next. My point is simply that these languages perhaps came about because of Java's stagnation. And actually I give a ton of credit to the JVM itself, i.e. its ability to accommodate all kinds of different languages. If we keep Java as the assembly language, OK fine. But there are still a lot of people who don't see anything wrong with Java and companies who won't move to anything more modern.

5. Checked exceptions do document what exceptions can be thrown. I agree. And for things like I/O they might increase the probability that resources get cleaned up appropriately. But closures would guarantee resource cleanup without reliance on the developer remembering to add that finally block, again, and again, and again. But mostly, I don't care what type of exception is thrown. And if I do, then I do the research (i.e. read the javadocs or source) to find out what can be thrown. And if I can actually do something about an exception I'll go ahead, catch it, handle it, and move on. Most times I can't do anything about exceptions though, e.g. in RDBMS-driven web apps.

6.Yes, certainly it would be great to update the libraries if closures were added. And yes that would be a large undertaking.

7. Ok, you got me. I didn't fully flesh out all the details of my new property syntax. How about property readwrite String firstName; or property readonly balance; ??? That's in about 5 seconds of thinking.

8. I don't know the details about Clojure's implementation, but if you can still use raw types then why would it matter if the compiler retains types on generic types? I am sure there is a reason...just that I don't know the internals well enough to know the answer. And as an application developer should I need to care?

9. Yes I know John Rose's explanation of method handles is different than what I wanted and called method/property handles. Essentially I would want to be able to refer to a property or method, just as my Hibernate Criteria query example showed. And I know exactly zero compared to John Rose. :-)

10. OK, I like adding multi-line strings too. I'm sure Jonathan Schwartz will take note. :-)

11. For local variables defined using var p = new Person(), why would you lose certain compile time info? How is there any way p could be anything other than a Person object? Again, I'm assuming my lack of lower-level compiler knowledge is probably shining through here. So are you then implying that Scala loses info like this too? And C#? var, def, whatever. A few new keywords are overdue anyway, if you amortize them over the lifetime of Java.

12. Ok, so go with the Person.new() syntax. :-)

I liked the idea of allowing multiple exception types in the same catch block. Then again catch (Exception) also works.

brweber2 said:

11) Type inference of the type you described would be fine in cases when you didn't want less specific compile time information about the type of the variable.

11. For local variables defined using var p = new Person(), why would you lose certain compile time info? How is there any way p could be anything other than a Person object? Again, I'm assuming my lack of lower-level compiler knowledge is probably shining through here. So are you then implying that Scala loses info like this too? And C#? var, def, whatever. A few new keywords are overdue anyway, if you amortize them over the lifetime of Java.

Type inference works just fine in most cases. In a few cases you want to explicitly declare the type of the variable instead of using the most specific known type at compile time. The simplest example of this is an interface Foo with an implementing class FooImpl:

Foo foo = new FooImpl();

vs.

var foo = new FooImpl();

As you know the compile time type of the variable foo is different in these two cases. All I am suggesting is that if you add type inference you should always be able to "back out" and explicitly provide the type yourself (assuming that you provide a valid type). I know that you are familiar with this concept, I am simply pointing it out.

Joao Assuncao said:

I agree with most of your suggestions to improve Java, except one, the removal of checked exceptions. I only started giving value to checked exceptions after working in C#. It's a pain to keep checking MSDN for the possible exceptions when I'm invoking some method, because if I forget, and the exception is not caught, the runtime kills the application. Checked exceptions enforce the developer to pay attention to errors.

I agree that most of the people don't handle checked exceptions correctly. Error handling is difficult and most of the time the reaction to an error is not specified in the use cases. So most of the developers just log the exception or, in the worst case, silence it. InterruptedException is a very good example of that.

To avoid polluting the code with try..catch blocks when a developer doesn't care with error handling, the language could offer something to acknowledge that fact. Maybe an annotation to make the compiler wrap the code inside a default handler, with the option in the annotation to specify an user defined error handler. This idea just popped in my head so probably there are a bunch of problems with it.

Sakuraba said:

Google will create the Noop language and we will all get rid of the insanity.

Andy Wilson said:

Application specific checked exceptions can be very useful, especially if you put meaningful data within it. You at a minimum know the kinds of critical errors that can occur within the application and present the user with a reasonable response and log the error message in a consistent fashion.
Unchecked exceptions require the developer to realize he's going to get them on occasion and have some top level handler to trap and log them. I've seen way too many applications that take this approach and end up telling the user than "An error has occurred. Please try again."

Your string interpolation example is missing the String.format(formatStr,...) method. Your logging statement could be rewritten to be:
log.error(String.format("The object of type [%s] and identifier [%s] does not exist.",foo.class.name,foo.id) , cause);

Of course, you may want to rewrite your logger to look more like this:
public String error(throwable t, String message, Object... details) {
String formattedUserMessage = "";
try{
if (details != null && details.length > 0){
formattedUserMessage = String.format(message, details);
} else {
formattedUserMessage = message;
}
} catch (RuntimeException re){
// because I'm ok with it
}

super.error(formattedUserMessage, t);
}


green tea said:

The Java language has been around for a pretty long time, and in my view is now a stagnant language.But it appears to have reached its evolutionary peak, and it doesn't look it's going to be evolved any further. This is not due to problems inherent in the language itself. Instead it seems the problem lies with Java's stewards (Sun and the JCP) and their unwillingness to evolve the language to keep it current and modern, and more importantly the goal to keep backward compatibility at all costs. Not just Sun, but also it seems the large corporations with correspondingly large investments in Java like IBM and Oracle aren't exactly chomping at the bit to improve Java.

Leave a comment

0 TrackBacks

Listed below are links to blogs that reference this entry: Can Java Be Saved?.

TrackBack URL for this entry: http://www.nearinfinity.com/mt/mt-tb.cgi/1532