I know quite a bit how to use C++-Templates — not an expert, mind you. With Java Generics (and Scala, for that matter), I have my diffuculties. Maybe, because I try to translate my C++ knowledge to the Java world. I read elsewhere, “they are nothing alike: Java Generics are only syntactic sugar saving casts, C++ Templates are only a glorified Preprocessor” 🙂
I am quite sure, both is a bit simplified a view. So, to understand the big and the subtle differences, I try to start with Specialization:
In C++ I can design a Template (class of function) that acts on any type T that supports my required operations:
template<typename T>
T plus(T a, T b) { return a.add(b); }
This now potentially adds the plus() operation to any type that can add().[note1][1]
Thus, if T supports the add(T) my template woll work. If it doesn’t,
The compiler will not complain as long as I do not use plus(). In Python
we call this “duck typing”: *If it acts like a duck, quacks like a duck,
it is a duck.* (Of course, with using type_traits this is modified a bit,
but as long as we have no concepts, this is how C++ Templates work, right?)
I guess, thats how Generics in Java work as well, isn’t it? The generic type I device is used as a “template” how to operate on any anything I try to put in there, right? As far as I understand I can (or must?) put some constraints on the type arguments: If I want to use add in my template, I have to declare the type argument to implement Addable. Correct? So, no “duck typing” (for better or worse).
Now, in C++ I can choose to specialize on a type that has no add():
template<>
T plus<MyX>(MyX a, MyX b) { return a + b; }
And even if all other types still can use the “default” implementation, now I added a special one for MyX — with no runtime overhead.
Is there any Java Generics mechanism that has the same purpose? Of course, in programming everything is doable, but I mean conceptually, without any tricks and magic?
No, generics in Java don’t work this way.
With generics you can’t do anything which would not be possible without Generics – you just avoid to have to write lots of casts, and the compiler ensures that everything is typesafe (as long as you don’t get some warnings or suppress those).
So, for each type variable you can only call the methods defined in its bounds (no duck typing).
Also, there is no code generation (apart from some adapter methods to delegate to methods with other parameter types for the purpose of implementing generic types). Assume you had something like this
Then we could create our
summethod with two arguments:The static method is compiled to the same bytecode like this (with additional type information in annotations):
This is called type erasure.
Now this method can be called for every pair of two elements of an addable type, like this one:
What here happens is that the compiler creates an additional synthetic method like this:
This method will only be called by generic code with the right types, this guarantees the compiler, assuming you are not doing some unsafe casts somewhere – then the
(Integer)cast here will catch the mistake (and throw a ClassCastException).The
summethod now always calls theplusmethod of the first object, there is no way around this. There is not code generated for every type argument possible (this is the key difference between Java generics and C++ templates), so we can’t simply replace one of the generated method with a specialized one.Of course, you can create a second
summethod like irreputable proposed (with overloading), but this will only be selected if you use theMyXtype directly in source code, not when you are calling thesummethod from some other generic code which happens to be parametrized with MyX, like this:Now
product(5, new MyX(...))will call oursum(T,T)method (which in turn calls theplusmethod), not any overloadedsum(MyX, MyX)method.(JDK 7 adds a new
dynamicmethod dispatch mode which allows specialization by every argument on run time, but this is not used by the Java language, only intended to be used by other JVM-based languages.)