Generic upper bounded wildcard instantiation known

2019-09-18 12:40发布

问题:

class Aliphatic<F> extends Organic<F>{}
class Hexane<G> extends Aliphatic<G>{}
public class Organic<E>{
    void react(E e){}
    static void main(String[] args){
        Organic<? extends Organic> compound = new Aliphatic<Organic>();
        compound.react(new Organic());
    }
}

Why can't I call react method with Organic argument? The generic type ot the reference <? extends Organic> says that the generic type of the instantiation canb either a Organic, or a subtype of Organic.

Is because the compiler doesn't know this instantiation generic type until runtime type an so, it does not bind any value to its generic criteria?

Why is this case working? Is this case the same?

public class WildcardCollection {
    public static void main (String[] args){
        WildcardCollection w = new WildcardCollection();
        w.myMethod(new Items("hola",1));     //Compile
        w.myMethod(new SubItems("nuevo",3)); //Compile
    }
    public <T extends Items> void myMethod(T a){ //Same as above
        System.out.println("hi: "+a);
    }
}
class SubItems extends Items {
    SubItems(){};
    SubItems(String s, int i){ super(s,i);}
}
class Items implements Comparable<Items> {
    private String name;
    private int value;

    public Items() {}

    public Items(String n, int i){ this.name = n; this.value = i;}
    public String getName(){ return this.name;}
    public int getValue(){ return this.value;}

    public String toString(){ return "(" + this.name + "-" + this.value + ")";}

    public int compareTo(Items i){
        return this.value - i.getValue();
    }
}

回答1:

Quite simply, if you have an object of some generic type with a type parameter T instantiated with a ? extends X wildcard then you can't call methods on the object that take parameters of type T because the compiler can't guarantee type safety. However you can call methods that return T (and assign the return value to a variable of type X). In your specific example it looks like this should be safe

Organic<? extends Organic> compound = new Aliphatic<Organic>();
compound.react(new Organic());

but remember that the compiler has to match the react call based on the declaration type (? extends Organic), it can't rely on what you've assigned on the RHS. If the compiler allowed this then it would also have to allow

Organic<? extends Organic> compound = new Aliphatic<Hexane<?>>();
compound.react(new Organic());

which is clearly not correct - it's exactly the same situation as

Collection<? extends Number> nums = new ArrayList<Float>();
nums.add(Integer.valueOf(1));

(all this is aside from the fact that since Organic is generic you need to say Organic<? extends Organic<?>> or similar rather than just Organic<? extends Organic>)