Let me show my quick example:
public class Main {
public static abstract class Food {
}
public static abstract class Fruit extends Food {
public String getJuice(){
return "juice";
}
}
public static abstract class Container<T extends Food> {
private T food;
public final T get(){
return food;
}
}
public static abstract class Crate<T extends Fruit> extends Container<T> {
}
public static void Bar(Crate crate) {
Food f = crate.get(); //compiles fine
//Fruit f2 = crate.get(); //can't compile
}
}
When given a raw type, crate.get()
returns Food
instead of Fruit
Crate
is declared as Crate<T extends Fruit> extends Container<T>
so Crate<Food>
is forbidden. I am just curious: why does the method T get()
not return Fruit
? Why is Crate<Fruit>
required?
This happens because get
is declared by Container
. When using the raw type Crate
, which has the effect of applying erasure to all of its (inherited) methods, the signature of public T get()
is erased to public Food get()
, because T
has an upper bound of Food
in its declaring class.
While Crate
narrows the upper bound of T
, which happens to be the return type of get
, it doesn't override that method. If that were the case, you would see different behavior:
public static abstract class Container<T extends Food> {
private T food;
public T get() {
return food;
}
}
public static abstract class Crate<T extends Fruit> extends Container<T> {
@Override
public T get() {
return super.get();
}
}
public static void bar(Crate crate) {
Food f = crate.get(); // compiles fine
Fruit f2 = crate.get(); // also compiles
}
Now, public T get()
is erased to public Fruit get()
, since it has been redeclared in a class where T
has an upper bound of Fruit
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With