Matt Raible has been building web applications for most of his adult life. He started tinkering with the web before Netscape 1.0 was even released. For the last 16 years, Matt has helped companies adopt open source technologies (Spring, Hibernate, Apache, Struts, Tapestry, Grails) and use them effectively. Matt has been a speaker at many conferences worldwide, including Devoxx, Jfokus, ÜberConf, No Fluff Just Stuff, and a host of others. Matt is a DZone MVB and is not an employee of DZone and has posted 148 posts at DZone. You can read more from them at their website. View Full User Profile

Java 5 Sucks according to iBATIS Founder

02.21.2008
| 10016 views |
  • submit to reddit

I stumbled upon Clinton Begin's blog this evening and found his only post about how much he hates Java 5:

Anyone who knows me has already knows that I'm no fan of Java 5. Honestly, since Java 5 was released, Java has dropped from 1st to 4th on my list of languages that I consider when starting a new application. It was such a disappointment to me, both because of the poor implementation of the new features, as well as the omission of some fairly basic features.
...
I'm looking to Ruby, Groovy and C# 3.0 before I look to Java. Not so much because those languages are better than Java 5, but more because Java 1.4 was better than Java 5. Java is going downhill at the hands of Sun and the JCP. Sad, sad, sad...

Clinton has some very good points in his rant. Unfortunately, I don't think anything is being done to fix them.

For those that don't know, Clinton is the inventor of iBATIS and one of the heros of the Java Community that took on .NET when they said had a version of the J2EE Petstore that was one-third the lines of code (LOCs) and 28 times faster. Most of the JPetStore links don't work anymore, but you can read the announcement on TSS.

Clinton is also one of those down-to-earth type of people I really enjoy hanging out with. I've enjoyed several beers with him at many conferences and have always enjoyed his perspective. However, there's something that smells about this rant of his. If he hates Java 5 so much, and loves Java 1.4, why doesn't iBATIS implement a 1.4 feature? An enhancement request to support for JDBC 3 Generated Keys in iBATIS has been open for almost 3 years! C'mon Clinton - it would've taken you less time to implement this than to write your rant. ;-)

References
Published at DZone with permission of Matt Raible, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Rick Hightower replied on Thu, 2008/02/21 - 2:29am

I had the "misfortune" of working with Java 5 and then going back to JDK 1.4 for 18 months to work with a great client.

All I can say was that it was painful going back to JDK 1.4.

Generics may not be perfect but I prefer type safe lists to random collection of gak. I much prefer List<Employee> getEmployees() to List getEmployees() especially when you find the that the method does not return exactly what you thought it did (employees example probably not the best).

I find all developers that I work with prefer Java 5+ and would not look forward to going back to JDK 1.4. In fact, I have never met a Java developer in person who prefers JDK 1.4 to Java 5.

I am for the evolution of Java. I don't want to go back to 1990s Java programming. Bring on closures, inferred typing, etc. "I say let... lets evolve, let the chips fall where they may."--FC 1999

Okay I read his post and... he makes some really good points about annotations. I don't agree with him about Generics per se (I thought it was a needed tradeoff for backwards compatibility), but I have never been happy with annotations. To each his own.

rick hightowercto of arcMindbloglinkedin

cowwoc replied on Thu, 2008/02/21 - 7:56am

Here is a simple proposal that makes old code run slower and new code usable with Reified Generics. I would love to see this implemented in Java7 in place of some of the other fishy RFEs which have been circulating.

 

Given:

List animals = getAnimals();
Animal animal = (Animal) animals.get(0);
List dogs = animals;
Dog dog = (Dog) dogs.get(0);

 

When Java7 encounters this code it translates it into:

List<Animal> animals = getAnimals();
Animal animal = animals.get(0);
List<Dog> dogs = Collections.safeCast(animals);
Dog dog = dogs.get(0);

 

Where Collections.safeCast() is defined as:

List<T> safeCast(List<S> source)
{
List<T> result = new ArrayList<T>();
for (S element: source)
{
result.add((T) S);
}
return result;
}

Yes, this means that old code will run slower, but who cares? The point is that it will still run, period. If anything it will encourage people to migrate to Reified Generics to see a performance boost. New code will be more efficient and will benefit from runtime access to the Generic type.

 

This approach has the following benefits:

1) The compiler can stop accepting raw types (it'll throw an error instead of a warning), forcing all new code to migrate. Remember, developers have had three years to migrate their code already!

2) You can do away with most/all of the new warnings introduced in Java5. No need to litter your code with @SupressWarnings all over the place.

3) Runtime access to Generic types with all the new flexibility this implies.

 

Is there is a consensus on this issue?

Artur Karazniewicz replied on Thu, 2008/02/21 - 8:03am

I don't consider that Java SE 5.0 is worse than Java 1.4. But, the fact is, some features added to JSE 5 came with bag of snakes.

Just few examples:

Autoboxing - cool isn't it? Yep unless You know some dirty details like how does it work with overloading. It could be pretty magical. The bottom line is - autoboxing and widening works, but widening and then autoboxing doesn't work. Add to this varargs and You will end up with deep crap.

Generics - not much to add. Yust look at Generics FAQ by Angelika Langer - 400+ pages... C'mon its something substantial wrong with this...

Varargs - those are fine, once You understand that basically they are arrays (and for example You cannot overload method with array and varargs!).

This is not that JSE iw worse. Just it's much more complicated than JSE 1.4...

Clinton Begin replied on Thu, 2008/02/21 - 10:45am

Note to self:  In the future, avoid abusing Blogger as a quick way to post a REALLY long response to a comment in someone else's blog... 

Matt, RE: "[If Clinton] loves Java 1.4, why doesn't iBATIS implement a 1.4 feature"

Who says I don't like 1.3 better?  j/k  It's just time dude, just time.  As you can see from my empty blog, I don't have a lot of it these days!  :)

Clinton

Guillermo Schwarz replied on Thu, 2008/02/21 - 2:53pm

If you don't like generics, simply don't use it.

 I don't like generics and therefore I don't use (much), but otherwise I prefer the enhanced for loop, it certianly makes code cleaner.

 Also the new enum is a big improvement.

 What I really miss in Java is closures. But please don't change the language. Make as easy as it is in Smalltalk, do not introduce cryptic syntax as for example:

 {int x, int y => x+y}

 Instead of this I would write:

int _(int x, int y) { retrurn x+y; }

A lot more Java like, isn't it? Notice the _ replacing the method name. Method of which class? An anonymous class of course.

And the purpose of this would be to have a method like this:

int f( int x, int k(int, int) ) {

     return k( x, x );

}

Which receives a k method as argument and is called like this:

System.out.println( f( 2, int _(int x, int y) { retrurn x+y; } ) );

 Or like this:

int k(int, int) = int _(int x, int y) { retrurn x+y; };

System.out.println( f( 2, k ) );

And then Java semantics shouldn't need to be changed.

cowwoc replied on Thu, 2008/02/21 - 3:08pm in response to: Guillermo Schwarz

Guillermo,

 

You misunderstand. I like the idea behind Generics and I *want* to use it. I just dislike the implementation of Generics in Java and I believe we would be better off cleaning that up because adding any more colorful features. Generics increased the language complexity and decreased readability in a big way. Like I said, I take issue with their design, not with the idea of Generics.

 

I would love for Sun to focus an entire release of Java purely on improving readability. 

Jim Hazen replied on Thu, 2008/02/21 - 11:20pm in response to: cowwoc

Actually...this code breaks with a runtime exception because not all Animals are Dogs. Didn't you get the warning about the dangerous cast? :)

If this were a simple fix it would have been done already. Plus there are times when you're using third-party code that won't be updated and that you don't have the src for. Unless you're willing to throw away this code, you can't just stop supporting backwards compatibility.

 

Clinton Begin replied on Fri, 2008/02/22 - 12:47am

>> you can't just stop supporting backwards compatibility.

 But you can imlement half a feature that creates more typing (in both senses of the word) with no additional type safety?  ;-)

Here's my follow-up on backward compatibility: 

 http://www.clintonbegin.com/2008/02/backward-compatibilitybe-damned.html

 Clinton

cowwoc replied on Fri, 2008/02/22 - 12:08am

Yeah, I noticed that too after posting it. I was hoping to post an alternative approach but like you said there doesn't seem to be an easy solution.

cowwoc replied on Fri, 2008/02/22 - 12:18am

Okay, let's give this another go.

 

Here is the original non-Generic code:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class JDK14
{
private static class Animal
{
}

private static class Dog extends Animal
{
}

public static void printAnimals(List animals)
{
for (Iterator animal = animals.iterator(); animal.hasNext();)
System.out.println(animal.next().getClass().getName());
}

public static void printDogs(List dogs)
{
for (Iterator dog = dogs.iterator(); dog.hasNext();)
System.out.println(dog.next().getClass().getName());
}

public static void main(String[] args)
{
List animals = new ArrayList();
animals.add(new Animal());
printAnimals(animals);
animals.clear();
List dogs = animals;
animals.add(new Dog());
printDogs(dogs);
}
}

Now, say someone compiled this in JDK 1.4 and then tried running it under JDK 1.5. In that case I believe the JRE should interpret the code as follows:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class JDK15
{
private static class Animal
{
}

private static class Dog extends Animal
{
}

public static void printAnimals(List<Object> animals)
{
for (Iterator<Object> animal = animals.iterator(); animal.hasNext();)
System.out.println(animal.next().getClass().getName());
}

public static void printDogs(List<Object> dogs)
{
for (Iterator<Object> dog = dogs.iterator(); dog.hasNext();)
System.out.println(dog.next().getClass().getName());
}

public static void main(String[] args)
{
List<Object> animals = new ArrayList<Object>();
animals.add(new Animal());
printAnimals(animals);
animals.clear();
List<Object> dogs = animals;
animals.add(new Dog());
printDogs(dogs);
}
}

Essentially raw types go away in 1.5 and get interpreted as T = Object. Does anyone remember what backwards-compatibility problems this approach introduces?

Jim Hazen replied on Fri, 2008/02/22 - 3:07am in response to: cowwoc

As far as I know this is essentially what happens. The problem wasn't with 1.4 classes being compatible with 1.5 it was with 1.5 classes working on Java hardware devices. The hardware couldn't be made to understand new parameterized types and with so many devices out there it was deemed unacceptable for 1.5 classes not to run on them.

Eventually I do think there will come a day where some backwards compatibility will be scrapped. That hardware evolves and is recycled so frequently that the benefits of moving forward will outweigh staying compatible. Should it have come with Java 5, who knows.

I do know that I much prefer Generics in their current incarnation than not having them at all.

Dimitris Andreou replied on Fri, 2008/02/22 - 8:23am

>The problem wasn't with 1.4 classes being compatible with 1.5 it was with 1.5 classes working on Java hardware devices

The problem was migration compatibility, read it up here:

http://gafter.blogspot.com/2004/09/puzzling-through-erasure-answer.html

cowwoc replied on Fri, 2008/02/22 - 11:26am in response to: Dimitris Andreou

I read Neal's explanation and if I understand him correctly the crux of the problem is:

 

  1. OldCode is compiled against OldLibraries that did not contain Generics, in JDK 1.4
  2. NewLibraries add Generics but we want to still be able to run OldCode against them, both running under JDK 1.5.
  3. OldCode passes a List into a library method. In OldLibraries that method took in a "List" but in NewLibraries it takes in a List<String>
  4. The above situation will fail because if you default the type parameters to Object, then OldCode tries invoking the method with a List<Object> instead of a List<String>.

 

So my question then is, now that Generics has been out for five years (3 years since JDK 1.5, 2 years in prototype phase), isn't it possible to degrade the performance (keeping compatibility!) of older code in exchange for Reified Generics? Couldn't you fix the above problem as follows?

  1. When OldCode tries invoking the new method, we run Collections.checkedList() to cast from List<Object> to List<String>, then proceed to invoke the method. I know this has a performance impact, but it should work.
  2. If two methods have the same name but their arguments different only by their Generic type there is an ambiguity as to which method OldCode should invoke. In such a case, NewLibrary should provide a method taking List<Object> for OldCode to invoke. Compilers will issue an error if there is method ambiguity and this method is missing. If we ever decide to drop 1.4 support altogether we could remove this error message.

What do you think?

Guillermo Schwarz replied on Fri, 2008/02/22 - 7:46pm

cowwoc,

I agree with you completely. Java generics complicate the language. The same happened with templates in C++ and C++ took at least 10 times more to learn than Java, which meant that templates made C++ unusable.

 Java today is still usable, you just need to stay away from the heavy use of Generics that STL was in C++.

 In order for Java to have closures, the return problem must be solved.

 For example in Smalltalk you can type:

a := true.

[ a ] whileTrue: [

     x := x + 1.

     (x > 10) ifTrue: [ a := false ].

].

(x > 5) ifTrue: [ ^x ].

 The same in Java would be:

 int f() {

a = true;

while ( a ) {

     x = x + 1;

     if (x > 10) { a = false; }

}

if (x > 5) { return x; }

}

 Passing the blocks as parameters:

 int f() {

a = true;

Block block1 = {

     x = x + 1;

     if (x > 10) { a = false; }

};

while ( a ) block1.run();

Block block2 = { return x; }; 

if (x > 5) block2.run();

}

How does Java know where the return returns from?

One idea is to have a variable pointing to the current context, so that intead of writing:

return x;

We would write:

currentContext.return( x );

 

Dimitris Andreou replied on Fri, 2008/02/22 - 11:20pm in response to: cowwoc

Reified generics is an option that Gafter also discusses in his blog:

http://gafter.blogspot.com/2006/11/reified-generics-for-java.html

It sounds doable, although it involves the creation of parallel APIs (like C# introduced a second set of collections to accommodate reified generics).

I don't think the ultimate solution could be as simple as a cast between List<Object> to List<T>, for some T, though (it would have been done already). But then, I also haven't spend as much time as Neal pondering on the relevant issues. Probably he has a straight-forward only-line answer, so why don't you forward your question to him?

cowwoc replied on Sun, 2008/02/24 - 12:25pm

Neal brought up good points:

- An object cannot be simultaenously a List<Object> and List<String>. Collections.checkedList() simply wraps the underlying list and executes checks at runtime.

1) Once you wrap the List<Object> in a List<String>, what's to prevent someone from adding a non-String to the underlying List<Object> while the List<String> is being used? You could have a special List<Object> implementation which throws an exception at insertion time if this happens but old code will now throw an exception at a different place than it used to (at insertion time versus at retreival time). This might or might not be a big deal but it's worth considering.

2) By wrapping behind the scenes, you're changing the identity of the object sent by the caller and received by the method. If they attempt to synchronize with each other on the object, for example, they'll be synchronizing on different things.

 

Ideas are most welcome :)

cowwoc replied on Sun, 2008/02/24 - 3:40pm

You can say many things about Neal Gafter, but it seems that "diplomatic" is not one of them. Today he posted a not too tactful reply to my request for feedback which I found both offensive and bordering on arrogance: http://gafter.blogspot.com/2006/11/reified-generics-for-java.html#c8374851026588442066

I fully appreciate the fact that Neal has more experience than most people in language evolution and that he might not have much time on his hands but I really don't feel this justifies the tone of his reply.

I now understand what Charles Nutter means when he talks about the Java "Ivory Tower": http://www.infoq.com/interviews/qcon-panel-java-future

In my view, there is absolutely nothing wrong with individuals bouncing ideas off one another without delving into the level of formalism discussed here: http://blogs.sun.com/darcy/entry/so_you_want_to_change

The latter document makes it pretty much impossible for any one person to make a difference in the future of Java because of the huge amount of overhead imposed on any proposal put forward. I appreciate the fact that we *need* this level of formalism before a feature ends up in the language but I don't think it is appropriate for the brainstorming phase (which is where we are now). The people who initially come up with proposals are not necessarily the same people who will drive it through JCP and produce all the required formalisms. JCP represents a tiny minority of the Java population. Implying that anyone who doesn't "go the mile" through JCP doesn't have the right to discuss language changes pretty much guarantees that you end up with a designed-by-a-commitee API as opposed to what the community at large is looking for.

There are already existing JSRs whose goal is to improve Generics. I just wanted to see if I could contribute to their efforts in a small part. In my view, we should be encouraging this kind of debate, not trying to stifle it. Just my 2 cents.

Dimitris Andreou replied on Mon, 2008/02/25 - 12:48am in response to: cowwoc

Keep in mind that Neal has been trying to answer all sort of questions/proposals for some years now - no surprise that his patience runs out. I do remember him trying endlessly to set the record straight at forum.java.sun.com, proving unsound numerous ad-hoc proposals of other members. In my opinion, you can trust Neal's opinion and exteme expertise on this matter: if he thinks that this case is unworthy of debate, then this is probably so.

cowwoc replied on Mon, 2008/02/25 - 1:12am

I trust Neal's expertise on the topic, I just didn't appreciate the tone of his reply. He could have been a lot more polite. That said, it would also be useful for someone (it doesn't necessarily have to be Neal) to post a comprehensive FAQ on the design decisions behind Generics and simply point people to it whenever such questions come up.

As I mentioned in my initial posts, I suspected that my proposal overlooked something obvious but I couldn't see it myself. I was simply curious what I was missing and what requirements were behind some of the design decisions of Generics by erasure.

I appreciate all the help you and others in the community have provided to this end. Thank you. 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.