Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Generics, support "Specialization"? Conceptual similarities to C++ Templates?

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?

like image 231
towi Avatar asked Apr 02 '11 11:04

towi


3 Answers

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

/**
 * interface for objects who allow adding some other objects
 */
interface Addable<T> {
   /** returns the sum of this object and another object. */
   T plus(T summand);
}

Then we could create our sum method with two arguments:

public static <T extends Addable<T>> T sum(T first, T second) {
    return first.plus(second);
}

The static method is compiled to the same bytecode like this (with additional type information in annotations):

public static Addable sum(Addable first, Addable second) {
    return first.plus(second);
}

This is called type erasure.

Now this method can be called for every pair of two elements of an addable type, like this one:

public class Integer implements Addable<Integer> {
    public Integer plus(Integer that) {
       return new Integer(this.value + that.value);
    }

     // private implementation details omitted
}

What here happens is that the compiler creates an additional synthetic method like this:

public Object plus(Object that) {
    return this.plus((Integer)that);
}

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 sum method now always calls the plus method 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 sum method like irreputable proposed (with overloading), but this will only be selected if you use the MyX type directly in source code, not when you are calling the sum method from some other generic code which happens to be parametrized with MyX, like this:

public static <T extends Addable<T>> product (int times, T factor) {
    T result = factor;
    while(n > 1) {
        result = sum(result, factor);
    }
    return result;
}

Now product(5, new MyX(...)) will call our sum(T,T) method (which in turn calls the plus method), not any overloaded sum(MyX, MyX) method.

(JDK 7 adds a new dynamic method 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.)

like image 66
Paŭlo Ebermann Avatar answered Nov 15 '22 17:11

Paŭlo Ebermann


no - but your particular problem is more of an overloading issue.

There's no problem to define 2 plus methods like these

<T extends Addable> 
T   plus(T   a, T   b) { .. }

MyX plus(MyX a, MyX b) { .. }

This works even if MyX is an Addable; javac knows that the 2nd plus is more specific than the 1st plus, so when you call plus with two MyX args, the 2nd plus is chosen. In a sense Java does allow "specialized" version of methods:

f(T1, T2, .. Tn)

f(S1, S2, .. Sn)

works great if each Si is a subtype of Ti

For generic classes, we can do

class C<T extends Number> { ... }

class C_Integer extends C<Integer>{ ... } 

caller must use C_Integer instead of C<Integer> to pick the "specialized" version.


On duck typing: Java is more strict in static typing - unless it is a Duck, it is not a duck.

like image 30
irreputable Avatar answered Nov 15 '22 17:11

irreputable


HI,

java Generics it's different from C++ template.

Example:

Java code:

 public <T> T sum(T a, T b) {
  T newValue = a.sum(b);
  return newValue;
 }

In java this code don't work because generics base is class java.lang.Object, so you can use only method of this class.

you can construct this methis like this:

public <T extends Number> T sum(T a, T b) {
  T newValue = a.sum(b);
  return newValue;
 } 

in this case the base of generics is class java.lang.Number so you can use Integer, Double, Long ecc..

method "sum" depend of implementation of java.lang.Number.

Bye

like image 38
m.genova Avatar answered Nov 15 '22 18:11

m.genova