Consider the following Java code:
public class Program {
static class Value {
public String getString() {
return "value";
}
}
static class BetterValue extends Value {
public String getBetterString() {
return "better_value";
}
}
static class Container<V extends Value> {
public final V value;
// EDIT #2: to mitigate the NPE:
public Container(V value) {
this.value = value;
}
}
static class BetterContainter<V extends BetterValue> extends Container<V> {
// EDIT #2: to mitigate the NPE:
public BetterContainter(V value) {
super(value);
}
}
public static void main(String[] args) {
// EDIT #2: to mitigate the NPE:
Container container = new Container(new Value());
System.out.println(container.value.getString());
// EDIT #2: to mitigate the NPE:
BetterContainter betterContainer = new BetterContainter(new BetterValue());
System.out.println(betterContainer.value.getBetterString());
}
}
When I try to compile, I get:
Error:(28, 49) java: cannot find symbol
symbol: method getBetterString()
location: variable value of type V
Shouldn't the field 'value' in BetterContainer erase to BetterValue? Why does it erase to Value for Container then?
EDIT: To mitigate the "don't use raw types" arguments and bring the question closer to what I'm acctually dealing with, suppose adding this code:
static class Consumer<C extends Container> {
protected final C container;
public Consumer(C container) {
this.container = container;
}
public void consume() {
System.out.println(container.getValue().getValue());
}
}
static class BetterConsumer<C extends BetterContainer> extends Consumer<C> {
public BetterConsumer(C container) {
super(container);
}
@Override
public void consume() {
System.out.println(container.getValue().getBetterValue());
}
}
How would I solve being able to do
System.out.println(container.getValue().getBetterValue());
without casting?
@Slaw's answer explains the why: when using raw types the V field in the Container class will be erased to Value, even if it is accessed from BetterContainer.
To fix the issue with the your consumer example, you need to add a type variable (the ? wildcard in this case) to the consumer classes:
static class Consumer<C extends Container<?>> {
protected final C container;
public Consumer(C container) {
this.container = container;
}
public void consume() {
System.out.println(container.value.getString());
}
}
static class BetterConsumer<C extends BetterContainer<?>> extends Consumer<C> {
public BetterConsumer(C container) {
super(container);
}
@Override
public void consume() {
System.out.println(container.value.getBetterString());
}
}
While not necessary in your example, you could also use a bound type variable:
static class Consumer<T extends Value, C extends Container<T>> {