One of the things I was excited to learn about in the new version of Java was how they added generics to the language. I didn’t expect it to be as powerful as C++ templates: that amount of generality, with its associated complications (e.g. having to separately compile separate instantations of the same template, or the parsing problems that arise), doesn’t seem like the Java way. So my guess was that, if a template calls a method on the parametrized type, it would require the parametrized type to inherit from a class (or interface, of course) providing that method. In particular, you wouldn’t be able to get much use out of, say, types that are nested inside the parametrized types: you wouldn’t be able to assume that, say, each type T has a nested type T::iterator.
And, it turns out, I was right! (Incidentally, they sensibly added covariant return types at the same time, to ameliorate some of the potential negative implications of this restriction.) But there’s one twist I hadn’t expected. It hadn’t been at the front of my mind that Java already had one built-in generic type, namely arrays. And what I had completely forgotten is that Java arrays aren’t type-safe! If, say, Manager is a subclass of Employee, then a Java compiler will happily let you pass an array of Managers to a function expecting an array of Employees. Which would be fine if Java had a notion of const arrays, but it doesn’t; and a (non-const) array of Managers doesn’t support the full interface of an array of Employees, because you can add an Employee to the latter, but not the former. Java’s solution is to nonetheless allow you to pass an array of Managers to a function expecting an array of Employees, but to throw an exception at run time if you try to stick a non-Manager Employee into the resulting array.
They did a similarly weird thing with generic types. Say that you have a generic type Generic that’s parametrized by a type T. (Which must inherit from some specific class: we’ll assume it’s Object.) Then you can write Generic in place of Generic<T>, and you can pass an object of any type Generic<T> to a function expecting an object of type Generic. As with arrays, an exception will be thrown if you do something too horribly un-type-safe to the object (though, I think, the details differ somewhat); but, as with arrays, it’s really the sort of thing that should be caught by a compiler in the first place.
So why was this conversion allowed? One motivation is that it would allow you to implement generics without changing the virtual machine; apparently, they decided to change the virtual machine anyways, however. (I don’t know the details here.) And it allowed them to provide generic versions of various classes/interfaces in the standard library without changing their names. And, well, I guess throwing exceptions in certain places where the compiler could catch your mistakes for you is just the Java way of doing things?
All grumbling aside, I’m glad that Java added generics. Non-type-safe collections are pretty passé, even if I wish the new collections were really type-safe instead of only sort of type-safe. The language isn’t so simple that adding generics increases its complexity very much. Part of me thinks it’s too bad that, as far as I can tell, we’re just not going to see in Java the incredible wealth of techniques that C++ templates allow. I expect people to use Java generics in some surprising ways, but I just can’t see how they’ll lead to anything as exciting as we’ve seen in C++. But I guess that’s okay: it’s hardly the only way in which Java is less exciting than C++, and it’s still a quite serviceable language.
There are no revisions for this post.