134

I have heard that the Java implementation of Generics is not as good as the C# implementation. In that the syntax looks similar, what is it that is substandard about the Java implementation, or is it a religious point of view?

johnc
  • 39,385
  • 37
  • 101
  • 139
  • looks like this question has already been asked - http://stackoverflow.com/questions/31693/differences-in-generics#31866 – serg10 Dec 10 '08 at 13:51
  • 1
    Comprehensive comparison [here](http://www.jprl.com/Blog/archive/development/2007/Aug-31.html), with some links. – Strelok Dec 10 '08 at 04:15

3 Answers3

183

streloksi's link does a great job of breaking down the differences. The quick and dirty summary though is ...

In terms of syntax and usage. The syntax is roughly the same between the languages. A few quirks here and there (most notably in constraints). But basically if you can read one, you can likely read/use the other.

The biggest difference though is in the implementation.

Java uses the notion of type erasure to implement generics. In short the underlying compiled classes are not actually generic. They compile down to Object and casts. In effect Java generics are a compile time artifact and can easily be subverted at runtime.

C# on the other hand, by virtue of the CLR, implement generics all they way down to the byte code. The CLR took several breaking changes in order to support generics in 2.0. The benefits are performance improvements, deep type safety verification and reflection.

Again the provided link has a much more in depth breakdown I encourage you to read

Ufuk Hacıoğulları
  • 37,978
  • 12
  • 114
  • 156
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • 1
    I've read your Strelok's link, Anders Hejlsberg's [interview](http://www.artima.com/intv/generics2.html), and Eric Lippert's [blog-post](http://blogs.msdn.com/b/ericlippert/archive/2009/07/30/generics-are-not-templates.aspx). But I still can't get it - how does C# not doing type erasure **and** reference types share same IL code ? Could you please expand on this a bit ? – Alexander Malakhov May 30 '12 at 09:46
  • @AlexanderMalakhov all reference types are a word size in assembly and can be manipulated with the same instructions hence they can share the same assembly pattern. Where would you expect them to differ? – JaredPar May 30 '12 at 17:18
  • I guess Java does exactly the same thing. Then, where type info is? How, for example, reflection can work ? – Alexander Malakhov May 31 '12 at 01:17
  • 3
    @AlexanderMalakhov each instantiation still has a unique type object it's just that the method slots all point to the same implementation. – JaredPar May 31 '12 at 01:18
  • It seems like it's going to be changed for java 10 :) – Ced Jun 30 '16 at 03:34
  • 1
    no such plans for java 10. wonder how much more performant it is! – Dragonborn Feb 05 '17 at 18:28
39

The difference comes down to a design decision by Microsoft and Sun.

Generics in Java is implemented through type erasure by the compiler, which means that the type checking occurs at compile time, and the type information is removed. This approach was taken to keep the legacy code compatible with new code using generics:

From The Java Tutorials, Generics: Type Erasure:

When a generic type is instantiated, the compiler translates those types by a technique called type erasure — a process where the compiler removes all information related to type parameters and type arguments within a class or method. Type erasure enables Java applications that use generics to maintain binary compatibility with Java libraries and applications that were created before generics.

However, with generics in C# (.NET), there is no type erasure by the compiler, and the type checks are performed during runtime. This has its benefits that the type information is preserved in the compiled code.

From Wikipedia:

This design choice is leveraged to provide additional functionality, such as allowing reflection with preservation of generic types, as well as alleviating some of the limitations of erasure (such as being unable to create generic arrays). This also means that there is no performance hit from runtime casts and normally expensive boxing conversions.

Rather than saying ".NET generics is better than Java generics", one should look into the difference in the approach to implement generics. In Java, it appears that preserving compatibility was a high priority, while in .NET (when introduced at version 2.0), the realizing the full benefit of using generics was a higher priority.

coobird
  • 159,216
  • 35
  • 211
  • 226
  • @Tom What does "Immunity from analysis" mean? – makerofthings7 Aug 15 '14 at 14:15
  • 16
    It means that, yes, you can say ".NET generics are better than Java generics". Java should have taken the approach do do a breaking change and properly implement generics way back when they had the opportunity to do so. – Mike Sep 15 '14 at 13:20
  • 3
    "Backward compatibility" is incorrect, they both are backward compatible. It was done for "Migration Compatibility". – kervin Apr 05 '15 at 01:37
  • @kervin hope you could elaborate it. – Vahid Hashemi Jun 07 '21 at 20:43
12

Also found this conversation with Anders Hejlsberg that may be interesting too. To summarize points that Anders Hejlsberg made with some additional notes: Java generics were made for maximum compatibility with existing JVM that led to few odd things versus implementation you see in C#:

  • Type erasure forces implementation to represent every generic parametrized value as Object. While compiler provides automatic casts between Object and more specific type, it does not remove the negative impact of the type casts and boxing on performance (e.g. Object is cast to specific type MyClass or int had to be boxed in Integer, which would be even more serious for C#/.NET if they followed type erasure approach due to user-defined value types). As Anders said: "you don't get any of the execution efficiency" (that reified generics enable in C#)

  • Type erasure makes information available at compile time not accessible during runtime. Something that used to be List<Integer> becomes just a List with no way to recover generic type parameter at runtime. This makes difficult to build reflection or dynamic code-generation scenarios around Java generics. More recent SO answer shows a way around it via anonymous classes. But without tricks, something like generating code at runtime via reflection that gets elements from one collection instance and puts it to another collection instance can fail at runtime during execution of dynamically generated code: reflection doesn't help with catching mismatch in List<Double> versus List<Integer> in these situations.

But +1 for the answer linking to Jonathan Pryor's blog post.

IgorK
  • 886
  • 9
  • 26