another java generic question

前端 未结 6 774
别跟我提以往
别跟我提以往 2020-12-08 17:39

I have the following class:

interface Able{/* ... */}
class A implements Able{/* ... */}

and I have

Map

        
相关标签:
6条回答
  • 2020-12-08 18:21

    declaration of

    Map<String,? extends Able> as; 
    

    means "any map, with String keys, and values being subtype of Able". So, for example, you can do following:

    Map<String,? extends Able> as = new HashMap<String, SubSubAble>();
    

    And now let's look at this example:

    Map<String,? extends Able> as = new HashMap<String, SubSubAble>();
    as.put("key", new A() );
    

    If it were correct, you'll finish having HashMap with content {"key", new A()} - which is type-error!

    0 讨论(0)
  • 2020-12-08 18:27

    The reference to java generics is good (jdk site).

    Indeed @Oli_Charlesworth gave a good answer, but maybe this one will be more complete.

    In a Collection<? extends Able> you can't insert anything that's right.

    If you have

    class A implements Able {...}
    

    and

    class B implement Able {...}
    

    Then, Collection<? extends Able> is a super type of both :

    Collection<A>
    Collection<B>
    

    Thus it is legal to write some statement like

    //Code snippet 01
    Collection< ? extends Able > list;
    Collection<A> listA;
    Collection<B> listB;
    list = listA;
    list = listB;
    

    That is indeed the reason why the wildcard notation Collection<? extends Able> exists.

    But, here things are getting more interesting :

    In a Collection<A> you can only insert objects that are A (including subclasses). Same for Collection<B>. In both you can't add something that is just Able. For instance :

    //Code snippet 02
    listA.add( new A() );  //valid at compile-time
    listA.add( new B() );  //not valid at compile-time
    listB.add( new B() );  //valid at compile-time
    listB.add( new A() );  //not valid at compile-time
    

    Thus, if you group what we saw in code snippets 01 & 02, you will understand that it's absolutely impossible for the compiler to accept a statement like :

    Collection< ? extends Able > list;
    list.add( new A() );         //not allowed, will work only if list is List<A>
    list.add( new B() );         //not allowed, will work only if list is List<B>
    

    So yes, the super type Collection< ? extends Able > doesn't accept to add anything. More general types offer the intersection of functionalities of subtypes, and, as such, less features that subtype. Here, we lose the ability to add A objects and B objects. Those feature will happen later in the hierarchy... and it even means that we can't add anything in the super class Collection< ? extends Able >

    Additional remark :

    Also, note that in a Collection<Able> you can add whatever you want like this :

    Collection< Able > list;
    list.add( new A() );         //valid
    list.add( new B() );         //valid
    

    But, Collection<Able> is not a superclass of Collection<A> and Collection<B>. It would mean, as with any inheritance relation, that subclasses can do whatever their superclass can do, as inheritance is specialization. So, this would mean that we could add A objects and B objects to both subclasses Collection<A> and Collection<B> and that is not the case. So as it's not a superclass you can't have :

    Collection<Able> list;
    Collection<A> listA;
    Collection<B> listB;
    list = listA;  //not valid because there is no inheritance hierarchy
    list = listB;  //not valid because there is no inheritance hierarchy
    

    Note that inheritance is a hyperonimic relation (generalization/specialization) and collections define a meronimic relation (container/containee). And it's a headache to combine both of them formally, even though it's somewhat used quite easily by the fuzzy creatures humans are, for instance in the french figure of speech : synecdocque. :)

    0 讨论(0)
  • 2020-12-08 18:35

    From http://download.oracle.com/javase/tutorial/extra/generics/wildcards.html:

    There is, as usual, a price to be paid for the flexibility of using wildcards. That price is that it is now illegal to write into [a wildcard-based container]. For instance, this is not allowed:

    public void addRectangle(List<? extends Shape> shapes) {
        shapes.add(0, new Rectangle()); // Compile-time error!
    }
    

    You should be able to figure out why the code above is disallowed. The type of the second parameter to shapes.add() is ? extends Shape-- an unknown subtype of Shape. Since we don't know what type it is, we don't know if it is a supertype of Rectangle; it might or might not be such a supertype, so it isn't safe to pass a Rectangle there.

    0 讨论(0)
  • 2020-12-08 18:37

    Collection<?> is the supertype for all kinds of collection. It is not a collection that can hold any type. At least that was my misunderstanding of the whole concept.

    We can use it where we don't care about the generic type, like in this example:

    public static void print(Collection<?> aCollection) {
      for (Object o:aCollection) {
        System.out.println(o);
      }
    }
    

    If we had chosen the signature instead:

    public static void print(Collection<Object> aCollection)
    

    we would have limited ourselves to collections of type Collection<Object> - in other words, such a method wouldn't accept a Collection<String> type value.

    So a Collection<?> type is not a collection that can take any type. It only takes the unknown type. And as we don't know that type (its unknown ;) ), we can never add a value, because no type in java is a subclass of the unknown type.

    If we add bounds (like <? extends Able>), the type is still unknown.

    You're looking for a declaration of a map, whose values all implement the Able interface. The correct declaration is simply:

    Map<String, Able> map;
    

    Let's assume we have two types A and B that subclass Able and two additional maps

    Map<String, A> aMap;
    Map<String, B> bMap;
    

    and want a, method that returns any map whose values implement the Able interface: then we use the wildcard:

    public Map<String, ? extends Able> createAorBMap(boolean flag) {
     return flag ? aMap: bMap;
    }
    

    (again with the constraint, that we can't add new key/value pairs to the map that is returned by this method).

    0 讨论(0)
  • 2020-12-08 18:39

    You can't insert any object of any type in a collection declared using the wild card '?'

    you can only insert “null”

    Once you declare a collection as List, the compiler can't know that it's safe to add a SubAble.

    What if an Collection<SubSubAble> had been assigned to Collection<Able>? That would be a valid assignment, but adding a SubAble would pollute the collection.

    How can elements be added to a wildcard generic collection?

    0 讨论(0)
  • 2020-12-08 18:40

    A good way to understand the issue is to read what the wildcard means:

    Map<String,? extends Able> as;
    

    "A map with keys of type String and values of one type that extends Able."

    The reason why add operations are not allowed is because they "open the door" to introduce different types in the collection, which would conflict with the typing system. e.g.

    class UnAble implements Able;
    Map<String,UnAble> unableMap = new HashMap<String,UnAble>();
    Map<String,? extends Able> ableMap = unableMap;
    ableMap.put("wontwork",new A()); // type mismatch: insert an A-type into an Unable map 
    

    A correct use of the the wildcard construction would be:

    Result processAble(Map<String,? extends Able>) { ... read records & do something ... } 
    
    Map<String,A> ableMap = new HashMap<String,A>;
    ableMap.put("willwork",new A());
    processAble(as);
    processAble(unableMap); // from the definition above
    
    0 讨论(0)
提交回复
热议问题