List如果从流中收集,可以包含多种不同的ChildTypes。

huangapple go评论75阅读模式
英文:

List<? extends SuperType> can contain multiple diffierent ChildTypes if collected from stream

问题

Java不允许将多个超类型的子类型放入列表中,如果列表的声明泛型类型为<? extends 超类型>。因为在这种情况下,列表被视为只读,以强制它仅包含其动态类型的对象。

//1 处被注释的行将返回编译错误。

我的问题是:

  • 为什么在 //2 处被注释的行可以编译?最终它将多个子类型对象放入了列表中。
  • 如果在这里不是问题,为什么在 //1 处会抛出编译错误?
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

class Scratch {
    static class Animal {}
    static class Dog extends Animal {}
    static class Cat extends Animal {}

    public static void main(String[] args) {
        List<Cat> cats = new ArrayList<>();
        List<Dog> dogs = new ArrayList<>();

        //List<? extends Animal> animalsOfTwoTypes = new ArrayList<Dog>(); 
        //animalsOfTwoTypes.add(new Dog()); //1

        List<? extends Animal> animals = Stream.of(cats, dogs)  //2
                .flatMap(Collection::stream)
                .collect(Collectors.toList());
    }
}
英文:

Java don't allow putting multiple subtype of a superType into a list if <? extends superType> is the declared generic type of the list. Because in this case lists are treated as read only to enforce it contains only objects of it's dynamic type.

Line commented by //1 going to return the compile error.

My question is:

  • why would line commented by //2 compiles? It eventually puts multiple subType objects into the list.
  • in case it's not a problem here then why throw a compile error at //1
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

class Scratch {
    static class Animal {}
    static class Dog extends Animal {}
    static class Cat extends Animal {}

    public static void main(String[] args) {
        List&lt;Cat&gt; cats = new ArrayList&lt;&gt;();
        List&lt;Dog&gt; dogs = new ArrayList&lt;&gt;();

        //List&lt;? extends Animal&gt; animalsOfTwoTypes = new ArrayList&lt;Dog&gt;(); 
        //animalsOfTwoTypes.add(new Dog()); //1


        List&lt;? extends Animal&gt; animals = Stream.of(cats, dogs)  //2
                .flatMap(Collection::stream)
                .collect(Collectors.toList());
    }
}

答案1

得分: 2

> 最终它会将多个 subType 对象放入列表中。

是的,但它们都是 Animal 的子类型。您并没有指定列表中元素的类型;编译器只是在检查列表的结果类型是否与变量类型兼容。

您可以这样编写:

List&lt;Animal&gt; animalsWithoutWildcard = Stream.of(cats, dogs)  //2
        .flatMap(Collection::stream)
        .collect(Collectors.toList());

这也是可以的。然后

List&lt;? extends Animal&gt; animals = animalsWithoutWildcard;

也是可以的,因为 Animal 列表也可以用作可以从中获取 Animal 实例的列表:animalsAnimal 实例的生产者,而 animalsWithoutWildcard 同时既是 Animal 实例的生产者又是消费者

关键不在于由 List&lt;? extends Animal&gt; 变量引用的内容不能被添加(除了 null 之外),而在于不能通过该变量向其中添加内容。因此:

System.out.println(animals.size());     // N
animalsWithoutWildcard.add(new Dog());  // 可以
System.out.println(animals.size());     // N + 1

// 但是这是一个错误,尽管它是相同的列表。
animals.add(new Dog());
英文:

> It eventually puts multiple subType objects into the list.

Yes, but they're all subtypes of Animal. You've not specified what the type of the elements in the list should be; all the compiler is doing is checking that the resulting type of the list is compatible with the variable type.

You could have written:

    List&lt;Animal&gt; animalsWithoutWildcard = Stream.of(cats, dogs)  //2
            .flatMap(Collection::stream)
            .collect(Collectors.toList());

and this would be fine also. And then

    List&lt;? extends Animal&gt; animals = animalsWithoutWildcard;

would also be fine, because a list of Animals can also be used as a list from which you can obtain instances of Animal: animals is a producer of Animal instances, whereas animalsWithoutWildcard is both a producer and a consumer of Animal instances.

The point isn't that the thing referred to by a List&lt;? extends Animal&gt; variable can't have things added to it (aside from null), it is that it can't have things added to it through that variable. So:

System.out.println(animals.size());     // N
animalsWithoutWildcard.add(new Dog());  // fine
System.out.println(animals.size());     // N + 1

// but this is an error, even though it&#39;s the same list.
animals.add(new Dog());

huangapple
  • 本文由 发表于 2020年9月22日 21:32:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/64010859.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定