 |
Generics Considered Harmful
Posted by arnold on June 27, 2005 at 10:53 AM | Comments (53)
I don't know how to ease into this gently. So I'll just spit it out.
Generics are a mistake.
This is not a problem based on technical disagreements. It's a fundamental language design problem.
Any feature added to any system has to pass a basic test: If it adds complexity, is the benefit worth the cost? The more obscure or minor the benefit, the less complexity its worth. Sometimes this is referred to with the name “complexity budget”. A design should have a complexity budget to keep its overall complexity under control.
Generics are way out of whack. I have just finished (well, nearly) my work in the fourth edition of The Java Programming Language. I am glad to say that David Holmes, not I, was the one who covered generics. Just reviewing it and reading the specifications was enough to put my brain through a cuisinart stuck on “pulse”.
We went through that chapter multiple times, consulting with several people who wrote the specs and are otherwise experts. We were only able to cover the highest level in the book, and it's still pretty hard to understand (although David exceeded himself in making it as comprehensible as possible).
Learning to use generified types can get very complicated. It's hard to understand why you cannot do some things without casts, for example. But writing generified classes is rocket science. Here's one that showed up at the very last minute: It's a bad idea to declare a type that returns an array of a type parameter. That is, you shouldn't do this:
interface Holder<T> {
T[] toArray();
}
Why, you ask? Well, the problem is that T might itself be a generic type. That is, someone might declare a Holder<Set<String>>. And, ... uh, hold on, I'm trying to remember the issue here...
Actually, I'm only mildly embarrassed to say that I've forgotten. But I remember that it took a few back-and-forths between David and the advising expert so that David — remember, David is the guy who has been writing a chapter on generics after several months of experimentation and research and over a year of thinking about how to approach it — could understand the problem. So our book recommends against it because it isn't good.
Although there is an exception. It's okay to do this if the method takes as a parameter an array of T or a Class<T> object:
T[] toArray(Class);
That's OK to do.
Which brings up the problem that I always cite for C++: I call it the “Nth order exception to the exception rule.” It sounds like this: “You can do x, except in case y, unless y does z, in which case you can if ...”
Humans can't track this stuff. They are always loosing which exception to what other exception applies (or doesn't) in any given case.
Or, to show the same point in brief, consider this: Enum is actually a generic class defined as Enum<T extends Enum<T>>. You figure it out. We gave up trying to explain it. Our actual footnote on the subject says:
Enum is actually a generic class defined as Enum<T extends Enum<T>>. This circular definition is probably the most confounding generic type definition you are likely to encounter. We're assured by the type theorists that this is quite valid and significant, and that we should simply not think about it too much, for which we are grateful.
And we are grateful. But if we (meaning David) can't explain it so programmers can understand it, something is seriously wrong.
So now we know it's complex. But if it really saved your programming butt a lot it could be worth it. So what does it save you? It saves you making mistakes, like putting a String in a list that should only contain Longs, or attempting to pull out a String from such a list.
But we have a demonstration proof that we can live without it, namely that we have for nearly a decade. Of course there are such bugs in code, and if you generify a bunch of code you might even find one or two that were waiting to bite you (unless that code is actually orphaned). But I have yet to find someone who believes this to be a major source of error in their code, compared to other problems.
So we have a feature whose complexities are high, whose learning curve is steep, and whose benefit is limited. And add to that the feature is ubiquitous -- with Java 5 it is nearly possible to write code that doesn't interact with generics.
The complexity of Java has been turbocharged to what seems to me relatively small benefit. I don't see that the value is there to justify the cost. Not that we can change things, but I think we should at least view it as an demonstration proof of the value of an explicit complexity budget against which features must be justified. Without such a budget, it feels like the JSR process ran far ahead, without a step back to ask “Is this feature really necessary”. It seemed to just be understood that it was necessary.
It was understood wrong.
Bookmark blog post: del.icio.us Digg DZone Furl Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment
-
I've found several bugs in my code because of generics. I was checking collections for items that could never be there, and putting actual objects instead of wrappers into collections. They didn't show up often at runtime for various reasons, but with generics I found them.
I think most people who use generics for a few months will get the hang of it. I definitely have. Especially, if you use a smart editor like IDEA or maybe Eclipse, it does a lot of the work for you, and you don't need to think much about it. (Although IDE support for variance could be improved in some important ways.)
Posted by: keithkml on June 27, 2005 at 01:08 PM
-
Generics are a great tool (and the benefit vastly exceeds the learning curve, and eventual deficiencies in the current design of Java5) when you really need them. And I very often need this, for example when I have complex collection structures like this:
/** [QualityParameter->HashMap[String->ConfigurationItem]] Calculation data, indexed by QP. */
private static final HashMap mapCalcData;
The variable above produced code that was difficult to read and maintain, with a boatload of typecasts and iterators in loops. (Notice that I developed an informal generics-like documentation for the structure of collections.) Now with Java5 it's much better:
/** Calculation data, indexed by QP. */
private final HashMap<QualityParameter,HashMap<String,ConfigurationItem>> mapCalcData;
The generic collection above, combined with enhanced-for, has made my code MUCH more readable and robust (even uncovered a couple bugs or bad practices). I have lots and lots of examples like that, because I'm often implementing high-performance, server-side runtimes when I don't have the luxury of wrapping every complex data structure in several type-safe container classes just to make code read better.
When used in public interfaces, generics are also invaluable to enforce correctness, prevent bugs and reduce testing effort. For example, in one project I have defined an interface that declares a method that return a Map<Integer, Object[][]>[]. Without generics, this method woul return a simple Map[], and there are dozens of classes implementing this method, but I don't need anymore to worry that somebody may be putting the wrong kind of object in the returned map, which would bomb a critical part of the app. This is especially important with teams when one developer defines an interface that others will implement.
I agree that Java5's generics does have limitations. It should be improved (hopefully in Mustang?) at least to the point that I could write fully generic and warning-free code. Right now, there are some idioms that you cannot code without a type-safety warning, such as creating arrays of generic types. I don't know what it takes to solve these issues -- more syntax sugar, extensions to the bytecode/VM spec, harder type inference in javac, whatever -- I just want it fixed.
P.S.: I was also a C++ programmer in a previous life, and the complexity of templates exceeds Java5's generics by orders of magnitude. I still remember losing a full hour to simply understand an horrendous, half-page-length compiler message generated by a bug in the expansion of deeply nested templates in STL classes, and sometimes there was no bug at all, just an incompatibility between older compiler and newer libraries requiring support for the very latest template black magic... so, I think Java is in a very good balance btw power and complexity.
Posted by: opinali on June 27, 2005 at 02:09 PM
-
The problem of the advent of generics is not that they are hard, is that they require a mindet shift from the "This will somehow work" to "I am sure this will work since I thought about it".
Anyway I somehow agree that the advantages of generics are too little compared to their complexity, just look at Bruce Eckel's research in the field.
I quite grasped java5 generics easily since I already was thinking in terms of covariance/contravariance/rich type systems, but I think this stuff is out of place in java.
Oh, and the worst thing is that I think there were some things that could have produced a much grater productivity boost with little or none mental effort, such as adding mixins.
Posted by: homersimpson on June 28, 2005 at 02:01 AM
-
/** [QualityParameter->HashMap[String->ConfigurationItem]] Calculation data, indexed by QP. */
private static final HashMap mapCalcData;
==============
Just a thought on the above code - I'm not sure if generics are the best solution for this. Wouldn't returning a wrapping class with methods like "getConfigurationItem(String)" be more helpful? There are places in PMD where I have code that returns Maps that contain Lists, and I always feel like I'm forcing too much information onto my client code. I need to clean those up...
Posted by: tcopeland on June 28, 2005 at 07:03 AM
-
Generics themselves have a high 'complexity cost' associated with them alone, but the greatest cost comes from the unwillingness to change what is 'Java' and break backwards compatibilities to simplify new features. Generics in Java 5 are "compiler fluff", and users must deal with a lot of warnings and flaws as result. For example, your T[] return above - arrays have tons of shortfalls with javagenerics, including several common array operations being completely illegal with generic types. This is not a flaw of generics (C# does not have these problems) - it is a problem in Java not accepting generics as a "Java" feature, only accepting it as a language feature alone.
Posted by: cyberakuma on June 28, 2005 at 12:34 PM
-
homersimpson said above
Oh, and the worst thing is that I think there were some things that could have produced a much grater productivity boost with little or none mental effort, such as adding mixins.
Java 5 does mixins. Check out the rapt project here on java.net. The documentation for mixins is here and yes, it supports generics.
And if your at javaone at the moment, these mixins are going to be discussed in the "apt uses of apt" BOF tonight at 7:30
Posted by: brucechapman on June 28, 2005 at 06:30 PM
-
Ok, so the help screen for posting a comment was bullshitting me when it says
Allowable html: a href,br/,p,b,strong,em,i,ol,ul,li,blockquote,pre
The mixin documentation is here https://rapt.dev.java.net/nonav/docs/api/net/java/dev/rapt/exploratory/mixin/package-summary.html#package_description
and the rapt project is here https://rapt.dev.java.net/
Posted by: brucechapman on June 28, 2005 at 06:34 PM
-
The problem is not generics which are proven technology for decades (see Ada) and well understood. The problem is also not recursive types as in enum«E extends enum«E»»¹ which just restricts the type parameter to subclasses of enum«E». The problem is the half hearted approach Sun has choosen to implement generics. The limitation of generic arrays for instance are just a side effect of type erasure. Microsoft's .NET VM did it better (although not completely satisfying). Sun better considers implementing generics at the VM level. To say "Generics are a mistake" is a short-sighted response. Generic concepts in general are powerful and intuitive. The perverted implementation in Java makes them appear harder than they are.
--------------------
1 Had to use french quotes here since the HTML processing messed this up.
Posted by: albert_bachmann on June 29, 2005 at 02:12 AM
-
tcopeland: Yes, I could have used wrapper classes, but like I stated, the cost of doing that was substantial for this particular program because that data structure could be very large.
Even in the 95% of the cases, where my collections don't have a million items, wrapper classes have a cost in making my code bigger and harder to maintain. The generic collections are as safe and efficient as wrapped collections, require zero extra code, and the resulting navigation of the collections is even more readable because I can use standard collection methods like get(), or even better, enhanced for. Of course I could emulate this by defining identical methods in my wrapper classes, and implementing Iterable in those classes...
There are many other great uses for generics too. Collections are just the bread-and-butter that is easier to demo, but my 'killer' case for generics is reifying type relationships that are otherwise implicit. See this example:
class Account<? extends Client>
class PremierAccount extends Account<PremierClient>
Here, I have an hierarchy of Clients and another Hierarchy of Accounts, and the two are tighly coupled by type: certain kinds of accounts require certain kinds of clients. But now I can use generics to create constraints binding the account type to the client type. The type-safety benefit exists here (methods like Account.getClient() will return the proper client type and the compiler won't let me create a PremierAccount for a non-Premier Client), but I consider an even bigger benefit the fact that the typing relationship between the two hierarchies is explicit in the code, in a way that beats any documentation, and (when CASE tools support Java5) might smooth even more the transition bwn modeling and code.
Posted by: opinali on June 29, 2005 at 11:02 AM
-
cyberakuma, albert_bachmann: I agree wholeheartedly that Java5's generics has several limitations that could be avoided. Even with the erasure model, we can do better, if we are prepared to require some changes in the VM level, or some compatibility-breaking in the APIs.
But I am happy with Java5 because backwards compatibility is a HUGE win for a language so entrenched as Java. In the last few months I ported several projects to adopt generics (and other Java5 features), and I could do that with a very small effort. Because generics (as well as most stuff from JSR201) is only syntax sugar, in the end of the day, the updated code is identical to the original code. It's using the same libraries, it's exporting the same non-generic signatures to non-generified client code (which will only report type-safety warnings), the performance is the same, everything is the same – I barely need to test the new code! In fact, the transition to J2SE5.0 is so smooth that my only constraint is being able to deploy the new runtime, so I only cannot do it when the deployment environment is using some old appserver from a three-letter vendor that lags behind in new J2SE support. And even in this case there are workaround, like RetroWeaver, javac -jsr14 (check it: it's undocumented but produces 1.4-compatible classes with generics!), and similar solutions. I would love to work only in all-new projects with no legacy code, libraries and components, but most often that's not the case...
Now, consider that we are 10 months past 1.5.0-FCS, and the adoption of Java5 is still modest (at least in my POV). If Sun/JCP had chosen a less compatible way, the adoption would be even slower. I still rememeber my C++ days when I had to wait years to actually use the latest cool stuff in the language due to the slow pace of compiler vendors, tool and library vendors, other developers (due to the big learning curve), and managers (due to the risk of changing "stuff that works")... all these factors being related to the language changes being very big, very complex and very risky.
My proposed solution: let's use releases 5.0 and 6.0 (Mustang) to stabilize and popularize generics, but for Dolphin (7.0), let's make generics substantially better even if this requires major rework of the VM and libraries, and forces people to move to generics if they haven't yet done so (e.g., dropping support for non-generic use of generic classes, like "ArrayList list", which is one of the major blockers for enhancements in Java5's generics.)
Posted by: opinali on June 29, 2005 at 11:24 AM
-
The problem is that generics don't get on with arrays. The solution is obvious: ditch arrays.
From a practical point of view, there's not a great deal of point in returning arrays from methods instead of collections of some form.
Posted by: tackline on June 29, 2005 at 05:40 PM
-
I agree with the above post, most people who are having trouble with generics are really having trouble mixing arrays and generics. The solution is simple, stop using arrays. The problems root cause is that arrays are broken, not generics!
Posted by: hlovatt on June 30, 2005 at 06:12 PM
-
If I understand it, the problem with T[] toArray() is that it's impossible to correctly implement such a method, due to type erasure--you can't new up an array of the parameterized type because you don't know what that type is. But if you explicitly provide the type at runtime by passing it in as a Class, you can use reflection to create the right kind of array. The bottom of this page gives details:
http://www.langer.camelot.de/Articles/Papers/JavaGenerics/ArraysInJavaGenerics.htm
Posted by: jcheng on June 30, 2005 at 09:21 PM
-
Generics are difficult to design, not that much to use. As such, most of people should avoid to write generic code, as its reuse potential is not worth the writing+debugging effort. However, it's extremely nice to have generic Lists and stuff like that already available, which are quite intuitive to use.
I think it was worth the trouble, to take a type-theory-savvy bunch of programmers to design a generic standard library, given the million of people who will reuse their code.
The same applied to the awfully, gratuitously complicated (if not broken) C++ template system: for most of applications, it would have been an error to create template classes, but it was quite nice to use the quite well designed (well, considering the language's features and flaws) STL. However, I reckon that the STL designers must have had a tough time...
Posted by: fab13n on July 01, 2005 at 01:56 PM
-
Here is a blog entry I wrote about this very problem a little over a year ago:
http://www.javarants.com/C1464297901/E1994239229/index.html
I actually like writing generic code now but I still think it makes it hard to learn for new programmers. Also the limitations are often strange feeling, like the get() method on Map not taking the Key type.
Posted by: spullara on July 01, 2005 at 07:38 PM
-
In a way I agree with the author on Generics being a complicated feature, but I disagree on it being difficult to use.
If I have not misunderstood, the point that the author is trying to make here is analogous to teaching Physics in the secondary education by avoiding Complex Numbers as much as possible.
In that light I do agree that even with my current (dare to say complete) knowledge of Generics I can make subtle mistakes at times. Um, actually I've only done it once so far, with which it is resolved by a blog entry.
The point is - to learn the whole theory about Generics is truly difficult, yes, but to use it in the common case is so trivial; in learning curve terms, the basics of Generics which are useful 99.99% of the time is FLAT - only for the last 0.01% do it get real steep.
To quote an evidence - although it could be considered to be a biased one - I learned to use the Java Language 5 years ago yet spent literally no time at all using it; after the release of J2SE 5 with Generics (and of course other minor yet cool features as well) I use it as my primary language for development.
P.S. I am a physicist after all. =P
Posted by: alexlamsl on July 02, 2005 at 12:33 AM
-
Thank you Ken! You're my hero. The only code I've ever seen where generics would have "saved" us was code that was crap anyway. In my experience, proper design almost always avoids situations where the wrong thing winds up in a collection, and actually testing the code seems to cover all the rest, including returning Maps from interfaces.
One of the things that attracted me to Java was its relative conceptual simplicity, especially compared to C++, and the direction it was headed at the time. It seems Java is repeating that history now. Sure that simplicity caused design compromises at times, and those compromises have costs. However, complexity has a cost too, and I think the line has been crossed where the cost of complexity (far?) outwheigs the costs we had when things were simple.
To me the problem is with the JCP, which winds up being a popularity contest. I'm not conviced that language design and evolution by popularity (or squeaky wheel) is a good idea. As a friend of mine says about democracy: "The masses are asses." I too think Java has done fine for 10 years with slow change by a small group of people/experts, and would prefer to keep it that way. Even that slow pace of change causes problems for projects that last 6 years.
Can we start a JSR to leave the language alone?
Posted by: ridder on July 02, 2005 at 08:30 AM
-
Well I found Generics a better way of handling collection. Earlier in my applications I had to worry about object coming into my functions. But not any more. The generics take care of having the correct object type. It speed up my development time and made my code look neat and easy and less buggy :)
Posted by: samirmishra on July 03, 2005 at 06:42 PM
-
I don't know of a time when I introduced a bug by _not_ using generics, and the type theoretic implications of generics is scary. Generics aren't the only tool in the toolbox. If I understand what generics do in Java, then you could accomplish the same thing with static analysis and put the added complexity outside of the language itself.
Posted by: jrlawson on July 03, 2005 at 11:55 PM
-
That "new programmers" have a hard time understanding generics is a very bad argument. "New programmers" will have a hard time understanding inheritance, interfaces, proxies, reflection, and any more or less advanced feature of a language. It is not generics, but programmers who can't or won't make the effort to understat what should be ditched.
As to the arguments about how the type-safety provided by generics is unnecessary, the same can be said for every static type safety feature of any language. Java has been statically type-safe from the start, except for the absence of generics; now that is fixed. If you don't think that type-safety is valuable, there are very nice dynamically-typed languages that compile to the JVM.
The arguments about complexity (complexity budget) are also bogus precisely because Java generics were designed to be optional. As such, they pass the most important complexity test for a feature: you only deal with the complexity if you use the feature.
Finally, the original blog post did not strictly talk about the benefits/drawbacks about Java generics. It was a rant about how the authors of a Java book had a difficult time understanding generics well enough to be able to explain them.
Posted by: juanco on July 06, 2005 at 06:59 AM
-
We could live without generics in the same way we could live without TV, radio, or the Internet. (iow yes, but why? It doesn't help make a case that generics are bad) I find generics very helpful in particular cases where you are modifying code - that contains collections - you haven't seen before (or don't remember). With collections you can see at a glance what's acceptable to stick in the collection. without collections you have no idea whatsoever. Case in point - the XmlBeansSerializer patch for Axis 1.2.1.
Also, extreme kudos for being so honest, "generics suck but um.. I can't remember why." (7th paragraph) heh heh... :_) Even though I disagree with you I like your writing style and look forward to your future technical blogs.
Posted by: markswanson on July 07, 2005 at 07:38 AM
-
Hmmm... I don't understand the hype in generics. I'm now in my 10th Java year and have never problems with typecasting in collections. And I think that the analyse to the complexity of generics in Java is correct. If it is to difficult to describe it in a reference book, it is to complex for the daily use. Remember the complexibility of C++ and don't follow all IT hypes.
Posted by: iskohlmann on July 07, 2005 at 07:39 AM
-
I'll throw my hat in for generics. They're extremely useful exactly because they're complex. Take this example. I recently wanted to define an interface structure for uniquely identified entities within namespaces. It required two interfaces:
public interface Unique<T extends Unique> {
public Id<T> getId();
}
public interface Id<T extends Unique> extends Comparable<Id<T>>, Serializable {
public String getValue();
public Class<T> getIdClass();
}
Note the circular reference in "Unique<T extends Unique>"
It means that the implementing class needs to provide another unique class as the namespace for it's Id's. The really freaky thing is that it can provide itself. :) These two interfaces enable a way to describe uniquely identified objects with heirarchical namespaces in a typesafe manner. How much complex code did that save?
Posted by: gbarton on July 07, 2005 at 08:25 AM
-
Ignoring arrays would be easier if varargs didn't depend on them.
Posted by: tjpalmer on July 07, 2005 at 08:37 AM
-
Just my 2 cents... I picked up and started using Java generics shortly after the release of Java 5. My only reference was the JVM API docs and a 6 page powerpoint deck showing some of the nastier things (such as the use of the '?' and how this differs from 'Object', etc).
Granted, my experiences from a previous life left me with memories of how to do things with C++ templates, but I just don't think that Java generics are that hard. IMHO, most people pick them up pretty quickly and it saves a few lines of code here and there in exchange for clarity and simplicity in the real code of a system.
I will admit that because it is a bigger lever, it tempts me to do things that I probably shouldn't do in most cases, such as creating collection structures that are 3+ levels deep. However, the lack of a typedef statement in Java makes such usage fairly painful. So, when I encounter the thought, "I wish Java had a typedef", I have trained myself to go back and reconsider what I was doing.
Posted by: tlaurenzo on July 07, 2005 at 08:54 AM
-
In the previous post, I meant to say:
"...it saves a few lines of code here and there and adds clarity and simplicity in the real code of a system."
Posted by: tlaurenzo on July 07, 2005 at 08:56 AM
-
I'd have to say that your complaint about generics is an nth order complaint about Java's design. It all comes down to "strict static typing doesn't really work all the time" and "type must not be confused with protocol" or "substitutability has nothing to do with type".
You only need generics in strictly typed function dispatch languages like C++ and Java. You don't need them in message passing dynamically typed languages like Smalltalk and Objective C.
To work around the first rule - "strict static typing doesn't really work all the time", we get hacks like templates and generics. Better to give up on the strict static typing idea and just accept that the world is messy.
To me, this is just more proof that Java the language was irredeemably broken at conception.
Posted by: tblanchard on July 07, 2005 at 09:16 AM
-
The trouble with generics--and this has been known for many years--is what I'd like to call the Elvis/Einstein boundary. (Google for Mort/Elvis/Einstein if you wonder why the King enters the picture...)
List<E>. solves a problem that Elvis had: What the !@#$ is in that collection?
But before he knows it, he is at
<T> int binarySearch(List<? extends Comparable<? super T>> list, T key).
That`s for Einstein, not Elvis.
This has nothing to do with type erasure or backwards compatibility. It just shows that too much static typing can be, well, too much.
The Java 1.0 designers knew this. The built-in array type E[] allows for potentially dangerous conversions that are caught (gasp) at run-time, with an ArrayStoreException.
When using generics, the trick is to achieve a similar balance. I use generics when it helps, but I switch to raw types when I get the "brain in blender" feeling.
Posted by: cayhorstmann on July 07, 2005 at 11:21 AM
-
Ken, I cannot agree more with your main point (that generics are adding complexity to the language). Here is a quote from the original Java Language Specification: "It is designed to be simple enough that many programmers can achieve fluency in the language" (the quote comes from here). And now consider this declaration interface Page> . Simple enough?
Posted by: dstrupl on July 07, 2005 at 12:38 PM
-
oops, sorry the declaration should read Page<K extends Comparable<? super K>>, I should not have done preview ;-)
Posted by: dstrupl on July 07, 2005 at 12:40 PM
-
opinali:
creating types for which you need to know their internal structure to read them (like a hashmap that associates people to bidimensional arrays of Company objects) is a blatant violation of encapsulation. Your problem is screeeming for a class.. especially if you have complex data structures.
Whether you use generics or not you should still wrap the datstructure or suffer the consequences of repeating code.
And yes, I did notice you were an ex-C++ programmer. Because the example is very non-OO. Java is an OO language.
I have to agree with the writer of this article the cost is much greater than the benefit.
Posted by: dog on July 07, 2005 at 04:05 PM
-
Great blog, Ken, and I couldn't agree with more. It's not that generics aren't useful, it's that their cost in terms of language complexity is too high. In my professional career I moved from Ada to C++ to Java (ignoring other languages before Ada), and I found the simplicity and elegance of Java to be a welcome relief. Learning -- and teaching -- the Java language was straightforward before generics. I wish Sun would change the process for allowing developers to vote for RFE's (Request for Enhancements), so that developers can vote against certain RFE's. I would have used one of my votes to vote against generics.
Posted by: jmoore on July 08, 2005 at 04:03 AM
-
While I do have difficulties, too, with some really convoluted generics I find generics in general very useful. Generics provide type safety where it has been missing before. Just like it is not possible in Java to convert arbitrarily between types (unlike in C where nobody stops you from converting a string into a pointer to a pointer to a float variable), this should be not possible with collections, too. It should not be possible to throw just anything into a collection, and find out at runtime what you're casting to hasn't been inside.
Especially with APIs I find generics very usefull, in example when a collection of something is returned, and I see cleary from the method signature of what will be in that collection.
Posted by: ctreber on July 08, 2005 at 06:01 AM
-
<T> int binarySearch(List<? extends Comparable<? super T>> list, T key)
Its a shame they did this IMHO. I would have preferred having to specify a seperate comparison object, contravariant typing, or even requiring a type to implement the comparible interface for itself (in addition of parent types), rather than this sort of ugly syntax.
I really had to think for a couple of minutes on the <? super T> part the first time I saw it, and I've been programming for many years. I pity the poor college student getting exposed to programming for the first time, sorting through the javadoc and trying to figure out what all of that really means.
opinali, I would agree they did a great job wedging a requirement for generics syntax into an existing language in a backward compatible way. I think the syntax is horrific (such as the common question 'why do you extend interfaces now'?), and I feel it would have been more proper to just break backward compatibility. As it is now, you have warnings and even errors on proper code, I barrier to entry that is much higher than it needs to be, and no real guarantees of type safety when dealing with legacy code (i.e. having the legacy code throw a typecast exception, rather than having the type sit in your collection until you attempt to read it). And I fear the complexity of the generics model as implemented and the volume of legacy code is such that generics will always be based on type erasure.
Posted by: cyberakuma on July 08, 2005 at 10:44 PM
-
I have to agree with the author. I believe everybody can solve a problem, but I do prefer people to solve a problem in a much simpler way. The syntax of generic is not very readable at all.
Posted by: fieldingy on July 13, 2005 at 11:25 AM
-
I can appreciate the rant, Ken, but do you really believe that being without generics for ten years is anything but a mistake? The C++ STL was out then. What's surprising is that the original Java team didn't learn from the C++ problems of introducing templates 10+ years after C++'s introduction. The biggest complexities in generics arise from the need to be backwards compatible with non-generic code.
Sure, there's a learning curve. But type safety is worth a lot.
PS: What's so hard about Enum>? Read it out loud: "The type parameter of Enum is a type which extends an Enum of that type". Which means that T has to be a proper subtype of Enum, exclusive of the Enum supertype. Fine, it's new. But it's certainly not rocket science to figure out what it is.
Posted by: torjohn on July 14, 2005 at 04:52 PM
-
While I agree that there are a few drawbacks to the implementation (specifically lack of runtime knowledge via reflection BECAUSE we are using erasure), I have changed all of my code over to use the Generics, enums, static imports, enhanced for loops, etc -- and the code is MUCH cleaner, much nicer, etc.
But, everyone seems to be missing the one MAJOR key. The one biggest reason that Generics are better than typecasting (besides aesthetics)...
ClassCastExceptions... We all know them. We all do perfect code, but they still sometimes leak through. I won't bother to mention how much corporate code I have seen with that problem.
With typecasting, the end user is the one that sees the crash and/or lockup caused by miss-casting.
With generics, it doesn't even make it to QA before fixing it.
That's the real advantage -- changes potential crashes from runtime to compile time. As this list is mostly java-fans, that's something you probably all see the benefit of.
Anymore, I get annoyed if I have to write code here or there that DOESN'T use generics (usually some older APIs that I have to integrate with). Outside of the corporate code, when looking at my own personal code, you will never find a single typecast again. I, for one, prefer to find the bugs instead of letting my end users find them for me.
And for those still trying to understand why we might like Generics... consider this sample...
HashMap map = new HashMap();
map.put(123, "test 1"));
map.put(new Integer(234), "test 2"));
for(Integer i : map.keySet())
System.out.format("Key %d: %s\n", i, map.get(i));
-- Malachi
Posted by: malachid on July 18, 2005 at 03:25 PM
-
hmm, that should have been HashMap<Integer,String> map = new HashMap<Integer,String>(); It appears that the preview/edit converts those so you have to redo the < everytime. Oh well.
Posted by: malachid on July 18, 2005 at 03:28 PM
-
I've been with Java since 1.01 and I agree with Ken. Generics are a horrible mistake.
I regret also that I find examples using HashMaps to be uncompelling, because gratuitous use of HashMaps has always been a sign of a weak design.
Once you get into HashMaps containing other HashMaps then I've lost all sympathy - your problems are entirely self inflicted. Like another poster suggested, Java is an OO language - there's almost always a better way than that.
The people who say that generics are easily readable seem to all (or mostly) have extensive c++ template backgrounds. I think that makes a poor criteria for learning Java (ie 'first learn STL').
On the other hand, I have a friend doing some large (Terabyte) database work and he says he's found 'one or two' places where they made his life easier. Personally I'd have preferred to see generics implemented the same way as Aspects (ie you run some other step which checks additional type B&D;), rather than polluting the language.
Posted by: rickcarson on July 18, 2005 at 06:08 PM
-
There is a trend afoot in Java development lately: that of utilizing open source libraries. The other trend is that often these libraries are not well documented, and make a habit of using collections and other such types in their APIs. Generics would save me time in those cases when some library API is expecting or returning a collection, and the docs don't make it clear exactly what that collection is all about. Does it expect that Set of "roles" to be the String name of the roles or instances of the Role class?
Posted by: adepue on July 19, 2005 at 11:11 AM
-
Ken, I'm sorry I haven't read this article until now. I have kept a link in my inbox until I had a free evening. Now, with pizza and wine, I get to read it....
Amen, brother!
Although it might be bad for business, I tell people that I won't do serious Java development for pay any more. I'll mentor programmers whose current project happens to be in Java, but I'm not "in the Java business" any more. (I've asked a friend to re-design by business cards so that 'Java Technologies' no longer appears.) Generics is the reason.
If I wanted to program in C++, I wouldn't have stopped doing that in 1997. It scared me then and it scares me now, even if it's dressed up as Java. I picked up a C++ textbook recently and it reads like so much Greek (with Mandarin, at least, I'd have a hope of understanding a character or two). Generics is the first path towards full-powered Java templates and then the whole language is doomed.
Does anyone need a Ruby on Rails programmer? Cheap?
Posted by: jbrains on August 25, 2005 at 06:27 PM
-
I do not claim to be any more knowledgeable in Java than the rest of the world of s/w programmers,
but I simply fail to understand, why it was necessary to complicate Java with the introduction of Generics.
I understand that the intention was good, but the implementation, sorry to say, is very cryptic.
I believe, even the expert eyes, will now find a Java 5 code, using generics, difficult to absorb in a glance.
The JSR group simply spoiled a beautiful language to support a minor feature in the language libraries.
Libraries are supposed to aid language programmers and not alter the language itself.
Generics implementation does just the opposite.
I find it difficult to understand, why the same thing could not be done using annotations,
a great facility in the new version.
With the generics, we are just trying to describe a collection of objects;
the notation is simply a metadata.
So, for example, we could annotate a Set in the following way:
@Generic(java.lang.Integer)
Set someSet = new HashSet();
We could have introduced a member, called elementType, in the Collection of type Class,
which would be set, on processing of this annotation.
For example, the above declaration, would made a call:
setElementType(java.lang.Integer.getClass() );
Anytime an addition is made to the collection, a type checking can be done with the
aid of elementType member. The default value of elementType can been Object, so as to
support legacy code.
For wild card instances, we can introduce a language keyword called "anyType"
instead of using the cryptic "?", which could result in code like:
@Generic(anyType extends Foo)
Set someSet = new LinkedHashSet();
I am sure there are enough smarter people who could enhance this syntax without touching the language.
Thank you Ken, for the guidance through your Book, and I vote for you - “Generics is a mistake”.
Posted by: vaibhavkhattri on September 14, 2005 at 12:24 AM
-
I have to agree. I think generics may kill Java. The value added / complexity ratio is really low. A lot of people argue that most of the value of generics comes from just using them with collections. This is true, but it's like boating near the top of the falls at Niagara. Before you know it, you are going over.
I don't think anyone mentioned this, but I believe the problem with arrays and generics is that the way array sub-classing works is completely incompatible with generic variance rules.
For example: (pretend {} are >: the other annoyance related to generics)
// valid assignment
Object[] array = new String[10];
// valid at compile time - fails at runtime
array[0] = 1;
// not valid. protects from above
List <Object> list = new ArrayList<Integer>();
//valid
List<? extends Object> list = new ArrayList<String>();
in other words, an String array is an Object array, but a String list is not a Object list. This is where, I think, we start going over the falls. Anyway, consider the following:
List<Object>[] array = new ArrayList<String>[10];
Should that compile? To be honest, I have no clue. According to array rules it should but by generic rules it should not. Originally, I thought generic arrays weren't going to be allowed. I'm not convinced they are now. Perhaps this is a way around the rule. In any event, arrays and generics don't mix.
Posted by: dubwai on October 24, 2005 at 02:37 PM
-
The page http://www.langer.camelot.de/Articles/Papers/JavaGenerics/ArraysInJavaGenerics.htm has been relocated to http://www.AngelikaLanger.com/Articles/Papers/JavaGenerics/ArraysInJavaGenerics.htm.
Posted by: langer4713 on January 14, 2006 at 09:52 AM
-
I think Generics are a great idea and find that they can eliminate bugs and clean up code very well; however, I find the way java implements them is very complex. For example the next four code snippets should work but they don’t and it is very annoying.
Example 1)
List[] a = new List[10];
Example 2)
//this will not compile
List a = new ArrayList();
Collections.sort(a);
//this will compile
List b = new ArrayList();
Collections.sort(b);
Example 3)
public static void main(String[] args) {
List a = Collections.emptyList();
filter(a); //compiles
filter(Collections.emptyList()); //does not compile
}
public static void filter(List stringList){
}
Example 4)
List a = new ArrayList(); //does not compile
// Compiles but performs poorly when working with “int” because of the auto boxing.
List b = new ArrayList ();
I have been working with C# generics and found then to be much simpler to work with. I believe one of the fundamental differences in C# is the type information is kept at runtime, but in java all the type information is thrown away once compiled.
Posted by: joeys on March 10, 2006 at 08:21 AM
-
I think Generics are a great idea and find that they can eliminate bugs and clean up code very well; however, I find the way java implements them is very complex. For example the next four code snippets should work but they don’t and it is very annoying.
Example 1)
List[] a = new List[10];
Example 2)
//this will not compile
List a = new ArrayList();
Collections.sort(a);
//this will compile
List b = new ArrayList();
Collections.sort(b);
Example 3)
public static void main(String[] args) {
List a = Collections.emptyList();
filter(a); //compiles
filter(Collections.emptyList()); //does not compile
}
public static void filter(List stringList){
}
Example 4)
List a = new ArrayList(); //does not compile
// Compiles but performs poorly when working with “int” because of the auto boxing.
List b = new ArrayList ();
I have been working with C# generics and found then to be much simpler to work with. I believe one of the fundamental differences in C# is the type information is kept at runtime, but in java all the type information is thrown away once compiled.
Posted by: joeys on March 10, 2006 at 08:22 AM
-
I think Generics are a great idea and find that they can eliminate bugs and clean up code very well; however, I find the way java implements them is very complex. For example the next four code snippets should work but they don’t and it is very annoying.
Example 1)
List[] a = new List[10];
Example 2)
//this will not compile
List a = new ArrayList();
Collections.sort(a);
//this will compile
List b = new ArrayList();
Collections.sort(b);
Example 3)
public static void main(String[] args) {
List a = Collections.emptyList();
filter(a); //compiles
filter(Collections.emptyList()); //does not compile
}
public static void filter(List stringList){
}
Example 4)
List a = new ArrayList(); //does not compile
// Compiles but performs poorly when working with “int” because of the auto boxing.
List b = new ArrayList ();
I have been working with C# generics and found then to be much simpler to work with. I believe one of the fundamental differences in C# is the type information is kept at runtime, but in java all the type information is thrown away once compiled.
Posted by: joeys on March 10, 2006 at 08:24 AM
-
I think Generics are a great idea and find that they can eliminate bugs and clean up code very well; however, I find the way java implements them is very complex. For example the next four code snippets should work but they don’t and it is very annoying.
Example 1)
List[] a = new List[10];
Example 2)
//this will not compile
List a = new ArrayList();
Collections.sort(a);
//this will compile
List b = new ArrayList();
Collections.sort(b);
Example 3)
public static void main(String[] args) {
List a = Collections.emptyList();
filter(a); //compiles
filter(Collections.emptyList()); //does not compile
}
public static void filter(List stringList){
}
Example 4)
List a = new ArrayList(); //does not compile
// Compiles but performs poorly when working with “int” because of the auto boxing.
List b = new ArrayList ();
I have been working with C# generics and found then to be much simpler to work with. I believe one of the fundamental differences in C# is the type information is kept at runtime, but in java all the type information is thrown away once compiled.
Posted by: joeys on March 10, 2006 at 08:31 AM
-
I think Generics are a great idea and find that they can eliminate bugs and clean up code very well; however, I find the way java implements them is very complex. For example the next four code snippets should work but they don’t and it is very annoying.
Example 1)
List[] a = new List[10];
Example 2)
//this will not compile
List a = new ArrayList();
Collections.sort(a);
//this will compile
List b = new ArrayList();
Collections.sort(b);
Example 3)
public static void main(String[] args) {
List a = Collections.emptyList();
filter(a); //compiles
filter(Collections.emptyList()); //does not compile
}
public static void filter(List stringList){
}
Example 4)
List a = new ArrayList(); //does not compile
// Compiles but performs poorly when working with “int” because of the auto boxing.
List b = new ArrayList ();
I have been working with C# generics and found then to be much simpler to work with. I believe one of the fundamental differences in C# is the type information is kept at runtime, but in java all the type information is thrown away once compiled.
Posted by: joeys on March 10, 2006 at 08:33 AM
-
Generics solve the wrong problem the wrong way.
I wish Java had a better way of defining a typed list but, sadly, the introduction of generics is not that better way.
A horrifying syntax, complex implementation (because it's only compile time fluff), and border-case problems that are difficult enough that experts need to discuss them - it's a complete disaster. I could go on but it should really be obvious to everyone.
Posted by: nheger on July 25, 2007 at 05:05 AM
-
No amount of usefulness will ever make a good enough excuse for such horrible syntax, especially considering there is no reason for it to be done this was.
Posted by: roman_i on July 25, 2007 at 09:56 AM
-
I came acorss this article after searching for "the need for generics in Java". I as some people said got hooked to Java during my college days in 1997 days because of elegance and simplicity. Now it is becoming more like C++ and totally unwieldy. Is there something in Java that I can't do without the usage of Generics. Is the byte code generated from Generics based java code more optimal? I am lost.
Posted by: ag_theone on August 01, 2007 at 04:51 AM
-
I have to agree with Ken on this one. Simple generics are great, but they very quickly become mindboggling.
Just yesterday, we hit this one. We have two types in our system, ContentArtifact and ContentItem that were involved. ContentItem is a subtype of ContentArtifact.
Method A had a return type of Collection<? extends ContentArtifact>
Method B had a return type of Collection<T> where T is bounded to extend ContentItem.
We wanted method A to call method B, and (among other stuff) return the collection that method B gave it.
Well, first we got some error like "the wildcard capture of ? extends ContentArtifact is not compatible with T extends ContentItem" or something like that.
Luckily, I'd spent the time to read Angelika Langer's site on generics (70+ pages printed out, at 8-point font, for this "simple and easy to grasp" topic). So after about 10 minutes of head-scratching, I said "ah ha! ? extents ContentArtifact could refer to any subtype of ContentArtifact, and the compiler can't guarantee that it will be type compatible to ContentItem".
So, the easy fix is to change the return type of method A to Collection<ContentArtifact> and drop the ? stuff. Now there's a totally inscrutable 6-line error message, again dealing with type erasure and wildcard captures and something about Heisenberg uncertainty. But Eclipse has a little yellow light bulb: it suggests casting the return of method B to Collection<ContentArtifact>. Viola, it works.
WTF???
Not to toot our own horns, but I'm the lead architect here, have a PhD in physics, and have been working daily in Java for 10 years and know it pretty well. The other guy is a very senior enterprise developer (wrote an email system that sends 600 million emails/year with almost no maintenance). If we can't get it, it's highly unlikely that the "average" developer will ever in our lifetimes be able to figure this stuff out.
Posted by: kdelong on August 03, 2007 at 11:04 AM
|