Why is this generic type not erasing to specified class

milin :

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?

waxwing :

@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>> {

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=366827&siteId=1