简体   繁体   中英

Java <? extends Collection>

I have the following Java code:

List<? extends Collection<String>> attributes = new ArrayList<HashSet<String>>(nrAttributes);

...

attributes.replaceAll((in) -> {
        List<String> out = new ArrayList<>(); 
        out.addAll(in); 
        return out;
    });

But it gives me a compiler exception (last out is red) saying:

Type mismatch: cannot convert from List to capture#2-of ? extends Collection

Command line says:

J:\WS\Java test>javac Main.java -Xdiags:verbose
Main.java:11: error: method replaceAll in interface List<E> cannot be applied to given types;
        attributes.replaceAll((in) -> {
                             ^
  required: UnaryOperator<CAP#1>
  found: (in)->{ Li[...]ut; }
  reason: argument mismatch; bad return type in lambda expression
      List<String> cannot be converted to CAP#1
  where E is a type-variable:
    E extends Object declared in interface List
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Collection<String> from capture of ? extends Collection<String>
1 error

ie the ? capture, might be a sub-type or sibling of List<String> , so it is unsafe to add a List<String> to a List<? extends Collection> List<? extends Collection> . The ...extends Collection doesn't really matter here.


Possible solution:

List<? extends Collection<String>> attributes = new ArrayList<HashSet<String>>(nrAttributes);

...

List<List<String>> attribs2 = new ArrayList<>();

attributes.forEach((in) -> {
    List<String> out = new ArrayList<>();
    out.addAll(in);
    attribs2.add(out);
});

Or:

List<Collection<String>> attributes = new ArrayList<>(nrAttributes);        

...

attributes.replaceAll((in) -> {
    List<String> out = new ArrayList<>();
    out.addAll(in);
    return out;
});

Your extends just ensures you that you will read a Collection<String> from your list. But you can't write it.

For a very good explanation see this link where i took the following from:

You can't add any object to List<? extends T> List<? extends T> because you can't guarantee what kind of List it is really pointing to, so you can't guarantee that the object is allowed in that List. The only "guarantee" is that you can only read from it and you'll get a T or subclass of T.

You defined your attributes variable as a list of something that extends a collection of strings . At compile-time, the compiler cannot decide if attributes is in fact an instance of List<TreeSet<String> , or a List<LinkedList<String> or even a List<Vector<String> . Consequently, the compiler does not allow you to modify the attributes collection because there is a risk that you are adding an ArrayList<String> to a collection of another collection of strings (say TreeSet<String> .

In general, if you have a List<? extends Something> List<? extends Something> , the compiler wont allow you to modify the collection (add/remove/clear/...). Any list typed with ? extends T ? extends T is read-only !

Compiling :

List<Collection<String>> attributes = new ArrayList<>(nrAttributes);

When you say List> you are saying "A List of a particular subtype of Collection" All such ? extends X are named internally as capture#n of ? extends X (the first time it's capture#1..., the second capture#2... and so on), but every time it's considered a particular type. For instance, the following code does not compile either, with the same error

Why do you need the generics for? the following example can contain any subclass of Collection of String (compiles, btw)

    List<? extends Collection<String>> attributes = new ArrayList<HashSet<String>>(nrAttributes);

    attributes.replaceAll((in) -> {
        List<String> out = new ArrayList<>();
        out.addAll(in);
        return out;
    });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM