6

Java generics look quite different from those available in Scala, although both were designed by Martin Odersky. From my point of view, the design of generics in Java is worse, for instance:

  • there is no possibility to specify variance
  • one can get around the previous limitation by using wildcards, but this means the burden of specifying variance goes on the caller instead of the library designer
  • one cannot use a type constructor in generics

What were the constraints in Java that forced Odersky to design this mechanism for generics instead of the more flexible one he devised for Scala? Was he just savvier a few years later or there were actual limitations due to Java?

1
  • 1
    IIRC, backwards compatibility was a major issue. If I get a few minutes, I'll look up an article I read on that... Oct 5, 2012 at 15:35

1 Answer 1

9

As @FrustratedWithFormsDesigner noted, backwards compatibility was the single biggest constraint. And the resolution they chose was to keep backward compatibility by using type erasure, i.e. removing all generic type information during compilation. This means a List<String> and a List<Integer> will become the same type at runtime (and the same as a pre-Java5 List). And it has a lot of negative consequences, including most of what you listed above.

Update

The constraints set by this are much tighter than one may naively think:

What we require is that the same client code works with both the legacy and generic versions of a library. This means that the supplier and clients of a library can make completely independent choices about when to move from legacy to generic code. This is a much stronger requirement than backward compatibility; it is called migration compatibility or platform compatibility.

Java implements generics via erasure, which ensures that legacy and generic versions usually generate identical class files, save for some auxiliary information about types. It is possible to replace a legacy class file by a generic class file without changing, or even recompiling, any client code; this is called binary compatibility.

(Java Generics and Collections, Chapter 5)

Binary compatibility is not always automatically guaranteed; section 8.4 of the same book details some cases where binary compatibility may break during genericizing legacy interfaces even if all general rules have been followed.

Another aspect of backward compatibility is adherence to a type system where not everything is an object. So in Java primitive types are out of scope for generics just as for collections, and you have boxing/unboxing to complicate life (whereas in Scala everything is an object).

A third legacy in Java is native arrays which just don't fit with generics:

  • arrays are always reifiable types whereas generic types are not;
  • arrays can store primitive types whereas (generic) collections can not;
  • array subtyping is covariant, whereas generic subtyping is invariant;
  • as a consequence, arrays are less type safe than generics, and one can argue that they should be declared obsolete in favour of generic collections and not used in new code.

All this means a lot more rules to adhere to, thus corner cases and compromises to make, limiting the power and simplicity of Java generics.

Several such decisions can be seen in retrospect as wrong, but there is no way back anymore, thus no possibility to fix them without breaking backward compatibility, which - rightly or not - has become a sort of holy cow in Java over the years. I conjecture that the story of Java generics most probably brought a lot of experience and food for thought for its own designers as well, and these experiences were surely reused during the design of Scala.

13
  • 6
    +1 "has a lot of negative consequences" is a very mild way to put it. I'd say "makes Java generics close to useless" :) Oct 5, 2012 at 15:45
  • 1
    This does not answer the question. I know that generics had to be implemented with type erasure for backwards compatibility. Yet, the same holds for Scala generics, and still generics in Scala are more powerful - definitely not close to useless! So, what other constraints were there?
    – Andrea
    Oct 5, 2012 at 16:01
  • 1
    @dasblinkenlight: I'd say the opposite: any other decision would have made generics close to useless. Backwards compatibility is a hugely important thing. Oct 5, 2012 at 20:43
  • 1
    @MichaelBorgwardt It was not backward compatibility that drove the decision, but a desire to make it a compiler-only feature - essentially, a desire to let you run new code on old JVMs. Microsoft's success with adding generics to C# and VB that required significant changes to CLR is a hint that Java designers took a wrong route with Generics. Oct 5, 2012 at 20:52
  • 2
    @PéterTörök My understanding is that it is forward, not backward, compatibility. Backward compatibility lets a newer JVM run code produced by an older compiler; forward compatibility lets your older JVM accept code produced by a newer compiler. Oct 5, 2012 at 21:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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