6

At first glance I thought the following makes sense:

interface Test<T> {
    T getValue(T n);
}

class Impl implements Test<Integer>{
    public Integer getValue(Integer n){
        return n;
    }
}

And it compiles properly so everything seems A-OK.

But then I thought about it some more, in the context of erasure, and it seems to me that the Test interface gets erased to this:

interface Test {
    Object getValue(Object n);
}

So how is Impl still able to implement Test?

kng
  • 587
  • 2
  • 9
  • It's _depressingly_ complicated, but tl;dr while the type information is not present in the core data structure for the signature, the class file _does_ hold metadata about the generics (which is why tricks like `TypeToken` work; you have to create a new anonymous class), and the compiler can check them. (You can even introspect them at runtime.) – chrylis -cautiouslyoptimistic- Jul 25 '20 at 01:08

2 Answers2

13

javac actually creates bridge methods for this:

class Impl implements Test<Integer>{
    @Override public Integer getValue(Integer n){
        return n;
    }
}

compiles to

class Impl implements Test {
    public Integer getValue(Integer n) { // overrides nothing!
        return n;
    }
    @Override @Synthetic @Bridge public Object getValue(Object n) { 
        return this.getValue((Integer)n);
    }
}

Note: Synthetic and Bridge are not real annotations, but the compiled class files do tag these methods as "synthetic" and "bridge".

By using these bridge methods, Java ensures that if you have an Impl, you can call Impl#getValue(Integer)Integer (if you know that it actually has type Impl), or you can call the "generic" Test#getValue(Object)Object, if you only know that it's a Test<?>.

blackgreen
  • 4,019
  • 8
  • 23
  • 41
HTNW
  • 22,326
  • 1
  • 24
  • 53
0

Here is one mindset to help you more about Generics: Think of generics as a strictly compile-time protection. None of this protection exists at runtime (it is the context of erasure). And why generics has to do this? To support legacy code before Java 5.

The compiler uses generic type information (<Integer> in your Test class) to make sure that your code doesn't put the wrong things into your getValue() method, not String, not Float, not Double, only Integer. Therefore, generics give you BOTH compile-time protection and runtime protection.

Vu Anh
  • 74
  • 7