55

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?

3

7 Answers 7

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.

19
  • 5
    Just to add, because of type erasure, retrieving generic information at runtime isn't possible unless you use something like a TypeLiteral google-guice.googlecode.com/svn/trunk/javadoc/com/google/inject/…
    – Jeremy
    Commented Nov 30, 2010 at 20:57
  • 47
    Meaning that new ArrayList<String>.getClass() == new ArrayList<Integer>.getClass() Commented Nov 30, 2010 at 21:20
  • 9
    @Job no it does not. And C++ uses a completelly different approach to metaprogramming called templates. It uses duck-typing and you can do useful things like template<T> T add(T v1, T v2) { return v1->add(v2); } and there you have a truly generic way of creating a function that add s two thing no matter what those things are, they just have to have a method named add() with one parameter.
    – Trinidad
    Commented Feb 7, 2011 at 22:25
  • 7
    @Job it is generated code, indeed. Templates are meant to replace the C preprocessor and for doing so they should be able to make some very clever tricks and are by themselves a Turing-complete language. Java generics are only sugar for type-safe containers, and C# generics are better but still a poor man's C++ template.
    – Trinidad
    Commented Feb 8, 2011 at 0:12
  • 3
    @Job AFAIK, Java generics don't generate a class for each new generic type, it just adds typecasts to the code that uses generic methods, so it's not actually meta-programming IMO. C# templates do generate new code for each generic type, that is, if you use List<int> and List<double> in C#'s world would generate code for each of them. In comparison to templates, C# generics still require to know which type you can feed it You cannot implement the simple Add example I gave, there's no way without knowing beforehand which classes it can be applied to, which is a drawback.
    – Trinidad
    Commented Feb 8, 2011 at 0:57
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.

11
  • 14
    The JVM does not need to support generics. This would be the same as saying that C++ has no templates because the ix86 real machine does not support templates, which is false. The problem is the approach the compiler takes to implement generic types. And again, C++ templates involve no penalty at run-time, there's no reflection being done at all, I guess the only reason that would be need to be done in Java is just poor language design.
    – Trinidad
    Commented Feb 8, 2011 at 1:18
  • 2
    @Trinidad but I did not say Java doesn't have generics, so I don't see how it's the same. And yeah, the JVM does not need to support them, just like C++ does not need to optimize the code. But not optimizing it would certainly count as "something wrong". Commented Feb 8, 2011 at 13:19
  • 3
    What I argued is that there's no need for the JVM to have direct support for generics for being able to use reflection on them. Others have done it so it can be done, the problem is that Java does not generate new classes from generics, the no reification thing.
    – Trinidad
    Commented Feb 8, 2011 at 14:05
  • 4
    @Trinidad: In C++, assuming the compiler given enough time and memory, templates can be used to do anything and everything that can be done with information that is available at compile time (since compilation of templates is Turing-complete), but there is no way to create templates using information which is not available before the program is run. In .net, by contrast, it is possible for a program to create types based upon input; it would be fairly easy to write a program where the number of different types that could be created exceeds the number of electrons in the universe.
    – supercat
    Commented Oct 22, 2012 at 15:19
  • 2
    @Trinidad: Obviously, no single execution of the program could create all of those types (or, indeed, anything beyond an infinitesimal fraction of them), but the point is it doesn't have to. It only needs to create types which are actually used. One could have a program which could accept any arbitrary number (e.g. 8675309) and from it create a type unique to that number (e.g. Z8<Z6<Z7<Z5<Z3<Z0<Z9>>>>>>>), which would have different members from any other type. In C++, all types that could be generated from any input would have to be produced at compile time.
    – supercat
    Commented Oct 22, 2012 at 15:32
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.

2
  • "Note that this overloading would not require reification, as overloading is a static, compile-time issue." Wouldn't it? How would it work without reification? Wouldn't the JVM have to know at runtime which type of set you have, in order to know which method to call?
    – Tyler
    Commented Jun 24, 2011 at 17:50
  • 2
    @MatrixFrog No. As I say, overloading is a compile-time issue. The compiler uses the static type of the expression to select a particular overload, which is then the method written to the class file. If you have Object cs = new char[] { 'H', 'i' }; System.out.println(cs); you get nonsense printed. Change the type of cs to char[] and you'll get Hi. Commented Jun 26, 2011 at 13:02
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.

4
  • You don't need to butcher every class. You just need to add a suitable factory class and inject it into AsyncAdapter. (And fundamentally this isn't about generics, but the fact that constructors aren't inherited in Java). Commented Oct 25, 2011 at 10:15
  • 13
    @PeterTaylor: A suitable factory class just shoves all the boilerplate into some other out of sight mess and it still doesn't solve the problem. Now every time I want to instantiate a new instance of Parser I'll need to make the factory code even more convoluted. Whereas if "generic" truly meant generic then there would be no need for factories and all the other nonsense that goes by the name of design patterns. It's one of the many reasons I prefer to work in dynamic languages.
    – user7146
    Commented Oct 30, 2011 at 19:56
  • 2
    Sometimes similar in C++, where you can't pass the address of a constructor, when using templates+virtuals - you can just use a factory functor instead. Commented Feb 3, 2018 at 0:11
  • Java sucks because you can't write "proptected" before a name of a method? Poor you. Stick to dynamic languages. And be especially wary of Haskell.
    – fdreger
    Commented Sep 10, 2019 at 12:16
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.

3
  • I do get annoyed when I get a warning for using HashMap instead of HashMap<String, String>.
    – Michael K
    Commented Feb 10, 2011 at 13:55
  • 1
    @Michael but that actually should be used with generics... Commented Apr 25, 2011 at 0:27
  • The array problem goes to reifiability. See the Java Generics book (Alligator) for an explanation. Commented Feb 10, 2012 at 0:14
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.

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.)

4
  • Nice rant. However, there are many libraries/frameworks which could exists without generics, e.g., Guice, Gson, Hibernate. And generics are not that hard once you get used to it. Typing and reading the type arguments is a real PITA; here val helps if you can use.
    – maaartinus
    Commented May 23, 2014 at 23:09
  • 1
    @maaartinus: Wrote that a while ago. Also OP did ask for "issues". I find generics extremely useful and like them a lot--much more now than then. However, if you are writing your own collection, they are very hard to learn. And their usefulness fades when your collection's type is determined at run time--when the collection has a method telling you the class of its entries. At this point generics don't work and your IDE will produce hundreds of meaningless warnings. 99.99% of Java programming doesn't involve this. But I'd just had a lot of trouble with this when I wrote the above. Commented May 25, 2014 at 0:33
  • Being both Java and C# developer, I am wondering why would you prefer Java collections? Commented Oct 9, 2014 at 18:49
  • Fine. But without generics this error would throw a ClassCastException really quick at run time with little time wasted. That really depends on how much run time occurs between program startup and hitting the line of code in question. If a user reports the error and it takes you several minutes (or, worst-case scenario, hours or even days) to reproduce, that compile-time verification starts looking better and better... Commented Nov 25, 2015 at 18:35

Not the answer you're looking for? Browse other questions tagged or ask your own question.