56

I have seen several times on this site posts that decry Java's implementation of generics. Now, I can honestly say that I have not had any issues with using them. However, I have not attempted to make a generic class myself. So, what are your issues with Java's generic support?

Joachim Sauer
  • 11,016
Michael K
  • 15,659

7 Answers7

58

Java's generic implementation uses type erasure. This means that your strongly typed generic collections are actually of type Object at runtime. This has some performance considerations as it means primitive types must be boxed when added to a generic collection. Of course the benefits of compile time type correctness outweigh the general silliness of type erasure and obsessive focus on backwards compatibility.

ChaosPandion
  • 6,313
28

Observe that answers that have already been provided concentrate on the combination of Java-the-language, JVM and the Java Class Library.

There is nothing wrong with Java's generics as far as Java-the-language is concerned. As described in C# vs Java generics, Java's generics are pretty much fine on the language level1.

What's suboptimal is that the JVM doesn't support generics directly, which has several major consequences:

  • the reflection-related parts of the Class Library can't expose the full type information that was available in Java-the-language
  • some performance penalty is involved

I suppose the distinction I am making is a pedantic one as Java-the-language is ubiquitously compiled with JVM as the target and Java Class Library as the core library.

1 With the possible exception of wildcards, which is believed to make the type inference undecidable in the general case. This is a major difference between C# and Java generics that isn't mentioned very often at all. Thanks, Antimony.

13

The usual criticism is the lack of reification. That is to say objects at runtime do not contain information about their generic arguments (although the information is still present on fields, method, constructors and extended class and interfaces). This means that you could cast, say, ArrayList<String> to List<File>. The compiler will give you warnings, but it will also warn you if you take an ArrayList<String> assign it to an Object reference and then cast it to List<String>. The upsides are that with generics you probably shouldn't be doing the casting, performance is better without the unnecessary data and, of course, backward compatibility.

Some people complain that you cannot overload on the basis of generic arguments (void fn(Set<String>) and void fn(Set<File>)). You need to use better method names instead. Note that this overloading would not require reification, as overloading is a static, compile-time issue.

Primitive types don't work with generics.

Wildcards and bounds are quite complicated. They are very useful. If Java preferred immutability and tell-don't-ask interfaces, then declaration-side rather than use-side generics would be more appropriate.

10

Java generics suck because you can't do the following:

public class AsyncAdapter<Parser,Adapter> extends AsyncTask<String,Integer,Adapter> {
    proptected Adapter doInBackground(String... keywords) {
      Parser p = new Parser(keywords[0]); // this is an error
      /* some more stuff I was hoping for but couldn't do because
         the compiler wouldn't let me
      */
    }
}

You wouldn't need to butcher every class in the above code to get it to work as it should if generics were actually generic class parameters.

5

I think other answers have said this to some degree, but not very clearly. One of the problems with generics is losing the generic types during the reflection process. So for example:

List<String> arr = new ArrayList<String>();
assertTrue( ArrayList.class, arr.getClass() );
TypeVarible[] types = arr.getClass().getTypedVariables();

Unfortunately, the returned types can't tell you that arr's generic types are String. It's a subtle difference, but it's important. Because arr is created at runtime the generic types are erased at runtime so you can't figure that out. As some stated ArrayList<Integer> looks the same as ArrayList<String> from a reflection standpoint.

This might not matter to the user of Generics, but let's say we wanted to create some fancy framework that used reflection to figure out fancy things about how the user declared an instance's concrete generic types.

Factory<MySpecialObject> factory = new Factory<MySpecialObject>();
MySpecialObject obj = factory.create();

Let's say we wanted a generic factory to create an instance of MySpecialObject because that's the concrete generic type we declared for this instance. Well Factory class can't interrogate itself to find out the concrete type declared for this instance because Java erased them.

In .Net's generics you can do this because at runtime the object knows it's generic types because the compiler complied it into the binary. With erasures Java can't do this.

5
  • compiler warnings for missing generic parameters that serve no purpose make the language pointlessly verbose, e.g public String getName(Class<?> klazz){ return klazz.getName();}

  • Generics don't play nice with arrays

  • Lost type information makes reflection a mess of casting and duct tape.

Steve B.
  • 706
1

I could say a few good things about generics, but that wasn't the question. I could complain that they're not available at run time and don't work with arrays, but that's been mentioned.

A big psychological annoyance: I occasionally get into situations where generics cannot be made to work. (Arrays being the simplest example.) And I cannot figure out whether generics can't do the job or whether I'm just stupid. I hate that. Something like generics ought to work always. Every other time I can't do what I want using Java-the-language, I know the problem is me, and I know if I just keep pushing, I'll get there eventually. With generics, if I become too persistent, I can waste a lot of time.

But the real problem is that generics add too much complexity for too little gain. In the simplest cases it can stop me from adding an apple to a list containing cars. Fine. But without generics this error would throw a ClassCastException really quick at run time with little time wasted. If I add a car with a child seat with a child in it, do I need a compile-time warning that the list is only for cars with child seats that contain baby chimpanzees? A list of plain Object instances starts to look like a good idea.

Generic'ed code can have a lot of words and characters and take up a lot of extra space and take a lot longer to read. I can spend a lot of time getting all that extra code to work right. When it does, I feel insanely clever. I've also wasted several hours or more of my own time and I have to wonder if anyone else is ever going to be able to figure out the code. I'd like to be able to hand it off for maintenance to people who are either less smart than myself or who have less time to waste.

On the other hand (I got a bit wound up there and feel a need to provide some balance), it is nice when using simple collections and maps to have adds, puts, and gets checked when written, and it usually does not add much complexity to the code (if someone else writes the collection or map). And Java is better at this than C#. It seems none of the C# collections I want to use ever handle generics. (I admit I have strange tastes in collections.)

KeesDijk
  • 8,968
RalphChapin
  • 3,320