The fact that it's taken this book so long to get published is just one indicator of the complexity of the language. I've also noticed that most other Java books have either taken a long time, or just briefly glossed over generics, and often both. I don't feel so bad about how long Thinking in Java 4e has taken, because I've been putting a great deal of effort -- and it has taken all of it -- to understand and cover the new features in the language. The generics chapter, even though I've already spent months on it, will be the last to get done and the chapter that will remain the hardest (possibly neck and neck with Concurrency).
One commenter to Ken's blog points out that it's not the idea of generics that is the problem, but the implementation, to which I agree, and I also think this is what Ken actually meant. I am very fond of the idea of generics/templates/parameterized types. They add a great deal of power to a language. That's why it was so frustrating to discover the limitations in the way it is implemented in Java. After a fair amount of back-and-forth I understand why the limitations are there. Heck, I practically agree with the idea of migration compatibility. Without something like that, too much would be broken (although you could probably argue that it would have been acceptable to break a bit more code rather than give in to erasure).
But I think the real question that Ken is posing is this: "if we had known from the beginning what generics would look like, would we have still said it was worth the cost?"
I periodically get dismissed when I suggest that generics are hard, by someone who hasn't delved into them very deeply but has instead been charmed by the thought of List<String>. How hard could that be? If you look at it simply, it seems all pluses and no minuses. The problem is that, despite what some of the promoters within Sun said, to the effect that everyone could just be a consumer of generic libraries so it would all be as simple as List<String>, you will want to write your own generics, and once you open that can of worms, it becomes far messier than you'd like to think.
Here's what I think is the best resource for Java Generic knowlege: Angelika Langer's Java Generics FAQ. Angelika is wicked smart, to the point where she thinks that a lot of things about both C++ Templates and Java Generics should be obvious (but not so obvious that she didn't write the FAQ), and even she occasionally has a hard time with this stuff.
As much as Java people like to dis C++ for just about everything, C++ templates are much more straightforward than Java generics, because they are consistent. With erasure, you have to constantly remind yourself that you only think you know what a particular parameterized type is. But because you don't really (except sometimes), it causes all kinds of confusion. And a programming language is supposed to be an abstraction to reduce confusion, so Ken's comment about complexity: "I don't see that the value is there to justify the cost," is legitimate.
Apparently Gosling had thoughts of including generics in Java 1.0, but someone had come up with a deadline and adding generics would have broken it (I would argue that the effort to add generics for J2SE5 was much greater than it would have been to have just fixed the language before releasing it in the first place). If they had put it in at the beginning, things would have been a lot different because it could have been consistent in the same way that C++ is consistent (and before you complain about templates, note that it's always been bad error messages that have made C++ templates difficult -- and this is being addressed in the next version of the language). But it wasn't and it isn't.
Despite all that, I kind of like generics now. At least, now that I've gotten over the formidable learning curve. Or mostly over it -- I think it will be something that I will always have to step back from and take a few deep breaths before diving back in. And note that the reason I like generics is not a rational one, but rather the nerd's satisfaction of having successfully tackled something that is capriciously complex, without giving up. But this is not generally a good thing, and I would rather spend my effort and energy producing something new and valuable rather than grappling with the deep-sea octopii of complexity.
As Ken points out, we are stuck with generics in their current form, even if one commenter hopefully suggested that everything could be substantially reworked for J2SE 7.0 (because of the pain of implementing generics the first time, and the pain that people will experience when adopting them, I think I can safely guess that this won't happen). I can live with them, and even do interesting things with them (as you shall see when Thinking in Java 4e finally comes out), but I'm starting to think that J2SE5 may represent the last of the big language changes to Java. For one thing, as soon as J2SE5 was officially released, a number of key compiler folks left Sun. But more importantly, I think that the ability for programmers to incorporate the new language features that have been appearing in Java may have reached a saturation point, at least for a few years to come. I can safely say that generics will slow them down.
I love Java. I love Ruby. I love SmallTalk. I kind of tolerate C++. There are reasons for each of these comments, and to be honest a lot of it has to do with how types are handled. What I like about Ruby and SmallTalk are that you can just use a type. As long as a method is there to back up a call, Ruby and SmallTalk just work. There is no need for generics in that case.
The practical reason for generics (as opposed to the theorhetical reason) is so that you can confidently write your code so that you can work with a container of items and execute some method. For latent typed languages like Ruby, Python, and SmallTalk, you don't need to do anything special. You don't have to specify an interface or object type to ensure that all items in a list implement the same method. Its nice to not have to worry about that and do all kinds of casting.
The theorhetical reason for generics is to specify the method of using a particular object/container without concern for the real type used. Usually they are needed for compiled languages to ensure that the code is obeying all the type contracts.
The reason I like Java over C++ has to do with the normal types. In C++ you have a certain level of automatic overriding so that one method can handle different cases, but there are a number of exceptions to that rule that are inconsistently applied accross compilers. Writing cross compiler code, much less cross-platform code is an undertaking that is not always worth the cost. With Java there is a much simpler basic type system that works.
When it comes to generics, yeah I have to echo what everyone else is saying. There are some pretty nifty things that you can accomplish with Java Generics, but the implementing code looks ugly. On the other hand there are some pretty nifty things you can do in C++ templates that you can't do with Java generics. I get the feeling that the Java Generics is more complicated than C++ templates, but that the basic Java type system is much simpler than the basic C++ type system.
Makes me wonder though, is all the buzz about generics and templates really arguing things like "Tastes great!" vs. "Less Filling!"? I mean, you use something else like a Ruby, Python, or good ol' SmallTalk and noone needs generics at all. They simply aren't missed.
BTW, the only reason why I don't have much of an oppinion on Python is because I haven't used it for anything yet. I'm using it for my SCons build file, but that's really not enough to gain a good oppinion on it.
A very good point. One of my first books, on pre-template C++, pointed out that the reason that we needed templates was because C++ didn't have a root-of-all-objects, so you couldn't write generic containers. Java could and did have reusable containers by having them work with Object, and if they had taken a more insightful step and made everything -- including primitives -- be derived from Object, then it would have been even more compelling (C# does something like this, but I prefer Python's approach: make everything an object, and if you need additional performance, write native code).
However, I wouldn't go so far as to say that generics are just for containers -- I made that same mistake when I first began learning C++ templates. Generics are easily explained in terms of containers, but the general concept is much more powerful. Just like polymorphism, interfaces, and some of the design patterns, the fundamental goal is to enable you to write code that works across more types. Containers are an excellent example of that, but only one. I have been using generics whenever appropriate in Thinking in Java 4e, and although they complicate the code, they also make it more general-purpose.
So to repeat: I very much like the concept of generics. I think it's an essential part of an OO language, in some form (as you point out, Ruby, Smalltalk and Python don't need it because they all use latent/duck typing, and I find that I usually prefer this approach because it allows my brain to work on the real problem rather than, as Bobby Wolf once put it so well, "arguing with the compiler"). I understand why the Java designers felt they had to implement generics using erasure. I just think it's a shame: it complicates the language and may even be the seed of the next language revolution -- a language like Nice seems appropriate, since it builds on the already-accepted JVM but with more powerful and sophisticated concepts. Jython is also being revamped and improved (with a grant from the Python Software Foundation). I could imagine a powerful collaboration between the two, to enable more rapid product-to-market times.
I just got through upgrading a part of our product to support generics and my experience was that about 5% of the effort (effort to understand as much as to design and implement) went into supporting this construct :
which covers about 95% of the cases where I have wished for generics in the past.
The hard bits to understand were constructs like <? super Number> that, to be honest, I still have trouble thinking of a case where I would actually _need_ a construct like this.
How much would we have lost if the support for generics in Java was limited to
That's the thing. At first they seem like they ought to be simple and that you shouldn't have to worry about covariance cases, and then it sneaks up and you do. Comparable, for example. Or it just won't work without introducing covariance, so you must know enough about it to recognized that you need it.
I don't think it could have been partially implemented as you suggest. Generics are so all-encompassing that you couldn't just put a toe in the water. They are a fundamental language feature, which is why it's really too bad Gosling had a failure of imagination and didn't put them in at the beginnig. C++ bit the bullet and made them work uniformly because Stroustrup and the committee saw how much of an impact a partial implementation would have. Java couldn't because they waited until they were forced by C# (note that virtually all the features in J2SE5 were responses to C# features -- the value of a competetive marketplace is shown once again).
Also note that because generics were back-engineered into Java instead of being designed into the language from the start, some of the containers cannot be made as robust as they should. For example, look at Map, in particular the methods containsKey(Object key) and get(Object key). If these classes had been designed with pre-existing generics, they would have used parameterized types instead of Object. In C++, the key type is always checked at compile time.
Thanks, Michael. I just skimmed it, but Scala does look interesting. It may be that in the end, languages built on top of the JVM might eventually supersede Java, as Java grows top-heavy with complexity. After all, that's how Java got started -- Gosling thought C++ was too complicated.
Correct. Yet the constant roar of people screaming to add ever more and more things to the core language is deafening. Just look at the java.net forums, everything from native support for all kinds of obscure protocols to operator overloading to an === operator to replace the equals method on objects. Now there's kids screaming about needing a built in relational database, Mustang has been announced to have a fullblown HTTP server and SOAP support built in (among others).
I'm starting to consider dropping Java and shifting to C# instead (I'm pragmatic enough to choose a language which actually brings a decent income).
> Correct. Yet the constant roar of people screaming to add > ever more and more things to the core language is > deafening. > Just look at the java.net forums, everything from native > support for all kinds of obscure protocols to operator > overloading to an === operator to replace the equals > method on objects. > Now there's kids screaming about needing a built in > relational database, Mustang has been announced to have a > fullblown HTTP server and SOAP support built in (among > others). > > I'm starting to consider dropping Java and shifting to C# > instead (I'm pragmatic enough to choose a language which > actually brings a decent income).
Re Java generics, I'll just repeat that I have visited probably close to a hundred Java teams in the last five years and no single one of them ever said that casting objects out of collections was a recurring source of errors for them.
> Re Java generics, I'll just repeat that I have visited > probably close to a hundred Java teams in the last five > years and no single one of them ever said that casting > objects out of collections was a recurring source of > errors for them.
That's part of the misconception -- "autocasting" for collections is just one of the side effects of generics. The real benefit of parameterized types is the ability to write code that can be applied to more types. The problem with the Java generics implementation is that the ability to do this is not as complete as it could be, so it falls short of what you can do in C++ (with templates) or Python (without).
Because the resulting implementation of Java Generics muddies the waters about just what generics are for, there's a lot of confusion. If, as you point out, generics are for autocasting with collections, then it's a lot of complexity for a relatively small benefit. There are resulting arguments for and against generics based on autocasting.
On the other hand, if generics are supposed to be a full-fledged parameterized type mechanism, the erasure implementation makes them fall short, and it's much more complicated to do something (if you even can) that would be fairly straightforward in C++ (and trivial in Python). So there is a whole other set of arguments for and against generics based on whether it's a useful parameterized type mechanism.
When you combine these two arguments, the resulting cacaphony is very hard to follow, because you never know if the arguer is talking about autocasting or full parameterized types -- and many "autocasting" proponents are unaware of the implications of parameterized types, and so are thinking that the argument is trivial (and will be in for a surprise the first time they run into the real issues of generics). The "autocasting" camp also believes that they can blithely consume generic classes that someone else has written, without ever needing to learn enough about generics to write their own. In my experience, this is not true, and everyone will need greater depth of understanding than that.
"Parametric polymorphism" - that's the term that has been with us for decades, and is what templates/generics are all about (or should be). ML has supported it with static type checking since 1972. C++ has since the mid-90s (although not "officially" until 1998). Languages like Lisp, Smalltalk, Python, and Ruby have supported it "for free" (with runtime checking) since they were born (Lisp was what, 1960?).
This is not a new concept.
I must admit that I find it disappointing that Java has essentially reduced the power of parametric polymorphism to an auto-casting feature, with some wildcarding thrown in for fun (but which isn't all that useful either). I have never known a Java developer to retrieve the wrong thing from a container, so auto-casting is no big deal. You just can't grok the power of parametric polymorphism if all you know is how Java does it (or doesn't). I'm not saying that 1.5 is not progress - it certainly is in many ways - but it's the hype that claims that it's more than it really is that is a disservice to the programming community, IMO. Generics are not what most people expect them to be.
I find it refreshing, despite the people who don't "get it" and give him a bad time, that someone like Bruce has dug into this issue and exposed its gnarly truths. Now that Ken Arnold has spoken up, maybe we can get beyond the hype.
This is also yet another instance that confirms the value of a good theoretical education as well as hands-on experience. A good course on the analysis of programming languages as taught at most schools (including mine) explores all of this and more, so you don't have waste time rediscovering things that have been well understood since before you were born. Indeed, C++ borrowed much from ML as well as other languages -- but only because Bjarne knew his stuff.
On the lambda-the-ultimate link mentioned earlier in this thread, the following quote is posted:
"I've been playing with LISP lately, and I think that what we need is a new programming language with LISP capabilities from one side and with C capabilities on the other. The LISP side would be a language that would allow programs to be executed at compile time, and thus allowing for infinite concepts (including object-oriented programming) and true expandability, and the C side would provide all the known stuff (pointers, structs etc) that are useful at run-time for truly optimized applications plus direct interfacing with O/S and other services."
I see Python as that language. At the moment I can't imagine anything I cannot do well and enjoyably either in Python, C++, or some combination of the two (they are easily combined). Despite the many years I have spent using Java, both professionaly and in education, I have lost interest in it. The only way I could get excited about Java at this point is through Jython. It's the platform, not the language, that makes Java viable, and I suppose that explains why there are other tools that build on the JVM coming out now.
I think it predates the Java wildcards but it's still a very good read.
From my very biased point of view it seems to me that Java and C# generics are so far from the power and flexibility of C++ templates that they can't really be compared at all. At least C# can boast a nifty implementation that avoids type erasure but ultimately it's not of much use beyond generic containers (which is not to say that generic containers aren't a useful thing in themselves.)