Why do some claim that Java's implementation of generics is bad?

JavaGenerics

Java Problem Overview


I've occasionally heard that with generics, Java didn't get it right. (nearest reference, here)

Pardon my inexperience, but what would have made them better?

Java Solutions


Solution 1 - Java

Bad:

  • Type information is lost at compile time, so at execution time you can't tell what type it's "meant" to be
  • Can't be used for value types (this is a biggie - in .NET a List<byte> really is backed by a byte[] for example, and no boxing is required)
  • Syntax for calling generic methods sucks (IMO)
  • Syntax for constraints can get confusing
  • Wildcarding is generally confusing
  • Various restrictions due to the above - casting etc

Good:

  • Wildcarding allows covariance/contravariance to be specified at calling side, which is very neat in many situations
  • It's better than nothing!

Solution 2 - Java

The biggest problem is that Java generics are a compile-time only thing, and you can subvert it at run-time. C# is praised because it does more run-time checking. There is some really good discussion in this post, and it links to other discussions.

Solution 3 - Java

The main problem is that Java doesn't actually have generics at runtime. It's a compile time feature.

When you create a generic class in Java they use a method called "Type Erasure" to actually remove all of the generic types from the class and essentially replace them with Object. The mile high version of generics is that the compiler simply inserts casts to the specified generic type whenever it appears in the method body.

This has a lot of downsides. One of the biggest, IMHO, is that you can't use reflection to inspect a generic type. Types are not actually generic in the byte code and hence can't be inspected as generics.

Great overview of the differences here: http://www.jprl.com/Blog/archive/development/2007/Aug-31.html

Solution 4 - Java

  1. Runtime implementation (ie not type erasure);
  2. The ability to use primitive types (this is related to (1));
  3. While the wildcarding is useful the syntax and knowing when to use it is something that stumps a lot of people. and
  4. No performance improvement (because of (1); Java generics are syntactic sugar for castingi Objects).

(1) leads to some very strange behaviour. The best example I can think of is. Assume:

public class MyClass<T> {
  T getStuff() { ... }
  List<String> getOtherStuff() { ... }
}

then declare two variables:

MyClass<T> m1 = ...
MyClass m2 = ...

Now call getOtherStuff():

List<String> list1 = m1.getOtherStuff(); 
List<String> list2 = m2.getOtherStuff(); 

The second has its generic type argument stripped off by the compiler because it is a raw type (meaning the parameterized type isn't supplied) even though it has nothing to do with the parameterized type.

I'll also mention my favourite declaration from the JDK:

public class Enum<T extends Enum<T>>

Apart from wildcarding (which is a mixed bag) I just think the .Net generics are better.

Solution 5 - Java

I'm going to throw out a really controversial opinion. Generics complicate the language and complicate the code. For example, let's say that I have a map that maps a string to a list of strings. In the old days, I could declare this simply as

Map someMap;

Now, I have to declare it as

Map<String, List<String>> someMap;

And every time I pass it into some method, I have to repeat that big long declaration all over again. In my opinion, all that extra typing distracts the developer and takes him out of "the zone". Also, when code is filled with lots of cruft, sometimes it's hard to come back to it later and quickly sift through all the cruft to find the important logic.

Java already has a bad reputation for being one of the most verbose languages in common use, and generics just add to that problem.

And what do you really buy for all that extra verbosity? How many times have you really had problems where someone put an Integer into a collection that's supposed to hold Strings, or where someone tried to pull a String out of a collection of Integers? In my 10 years of experience working at building commercial Java applications, this has just never been a big source of errors. So, I'm not really sure what you're getting for the extra verbosity. It really just strikes me as extra bureaucratic baggage.

Now I'm going to get really controversial. What I see as the biggest problem with collections in Java 1.4 is the necessity to typecast everywhere. I view those typecasts as extra, verbose cruft that have many of the same problems as generics. So, for example, I can't just do

List someList = someMap.get("some key");

I have to do

List someList = (List) someMap.get("some key");

The reason, of course, is that get() returns an Object which is a supertype of List. So the assignment can't be made without a typecast. Again, think about how much that rule really buys you. From my experience, not much.

I think Java would have been way better off if 1) it had not added generics but 2) instead had allowed implicit casting from a supertype to a subtype. Let incorrect casts be caught at runtime. Then I could have had the simplicity of defining

Map someMap;

and later doing

List someList = someMap.get("some key");

all the cruft would be gone, and I really don't think I'd be introducing a big new source of bugs into my code.

Solution 6 - Java

Another side effect of them being compile-time and not run time is that you can't call the constructor of the generic type. So you can't use them to implement a generic factory...


public class MyClass {
public T getStuff() {
return new T();
}
}

--jeffk++

Solution 7 - Java

Ignoring the whole type erasure mess, generics as specified just don't work.

This compiles:

List<Integer> x = Collections.emptyList();

But this is a syntax error:

foo(Collections.emptyList());

Where foo is defined as:

void foo(List<Integer> x) { /* method body not important */ }

So whether an expression type checks depends on whether it is being assigned to a local variable or an actual parameter of a method call. How crazy is that?

Solution 8 - Java

Java generics are checked for correctness at compile time and then all type information is removed (the process is called type erasure. Thus, generic List<Integer> will be reduced to its raw type, non-generic List, which can contain objects of arbitrary class.

This results in being able to insert arbitrary objects to the list at runtime, as well as it's now impossible to tell what types were used as generic parameters. The latter in turn results in

ArrayList<Integer> li = new ArrayList<Integer>();
ArrayList<Float> lf = new ArrayList<Float>();
if(li.getClass() == lf.getClass()) // evaluates to true
  System.out.println("Equal");

Solution 9 - Java

The introduction of generics into Java was a difficult task because the architects were trying to balance functionality, ease of use, and backward compatibility with legacy code. Quite expectedly, compromises had to be made.

There are some who also feel that Java's implementation of generics increased the complexity of the language to an unacceptable level (see Ken Arnold's "Generics Considered Harmful"). Angelika Langer's Generics FAQs gives a pretty good idea as to how complicated things can become.

Solution 10 - Java

I wish this was a wiki so I could add to other people... but...

Problems:

  • Type Erasure (no runtime availability)
  • No support for primative types
  • Incompatability with Annotations (they were both added in 1.5 I'm still not sure why annotations don't allow generics aside from rushing the features)
  • Incompatability with Arrays. (Sometimes I really want to do somthing like Class<? extends MyObject>[], but I'm not allowed)
  • Wierd wildcard syntax and behavior
  • The fact that generic support is inconsistant across Java classes. They added it to most of the collections methods, but every once in a while, you run into an instance where its not there.

Solution 11 - Java

Java doesn't enforce Generics at run time, only at compile time.

This means that you can do interesting things like adding the wrong types to generic Collections.

Solution 12 - Java

Java generics are compile-time only and are compiled into non-generic code. In C#, the actual compiled MSIL is generic. This has huge implications for performance because Java still casts during runtime. See here for more.

Solution 13 - Java

If you listen to Java Posse #279 - Interview with Joe Darcy and Alex Buckley, they talk about this issue. That also links to a Neal Gafter blog post titled Reified Generics for Java that says:

> Many people are unsatisfied with the > restrictions caused by the way > generics are implemented in Java. > Specifically, they are unhappy that > generic type parameters are not > reified: they are not available at > runtime. Generics are implemented > using erasure, in which generic type > parameters are simply removed at > runtime.

That blog post, references an older entry, Puzzling Through Erasure: answer section, that stressed the point about migration compatibility in the requirements.

> The goal was to provide backwards > compatibility of both source and > object code, and also migration > compatibility.

Solution 14 - Java

The problem with Generics is that IMO its made reading and understanding method signatures in Java APIs 10 times harder for not a lot of gain in terms of robustness. In fact possibly they should have gone the other way and got rid of compile time type compatibility checks altogether with methods calls and left it to the developers to get right or deal with runtime exceptions.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionsdellysseView Question on Stackoverflow
Solution 1 - JavaJon SkeetView Answer on Stackoverflow
Solution 2 - JavaPaul TomblinView Answer on Stackoverflow
Solution 3 - JavaJaredParView Answer on Stackoverflow
Solution 4 - JavacletusView Answer on Stackoverflow
Solution 5 - JavaClint MillerView Answer on Stackoverflow
Solution 6 - JavajdkoftinoffView Answer on Stackoverflow
Solution 7 - JavaNatView Answer on Stackoverflow
Solution 8 - JavaAnton GogolevView Answer on Stackoverflow
Solution 9 - JavaZach ScrivenaView Answer on Stackoverflow
Solution 10 - JavaLaplie AndersonView Answer on Stackoverflow
Solution 11 - JavaPowerlordView Answer on Stackoverflow
Solution 12 - JavaSzymon RozgaView Answer on Stackoverflow
Solution 13 - JavaKevin HakansonView Answer on Stackoverflow
Solution 14 - JavaSR BhaskarView Answer on Stackoverflow