英文:
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<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());
}
}
答案1
得分: 2
> 最终它会将多个 subType 对象放入列表中。
是的,但它们都是 Animal
的子类型。您并没有指定列表中元素的类型;编译器只是在检查列表的结果类型是否与变量类型兼容。
您可以这样编写:
List<Animal> animalsWithoutWildcard = Stream.of(cats, dogs) //2
.flatMap(Collection::stream)
.collect(Collectors.toList());
这也是可以的。然后
List<? extends Animal> animals = animalsWithoutWildcard;
也是可以的,因为 Animal
列表也可以用作可以从中获取 Animal
实例的列表:animals
是 Animal
实例的生产者,而 animalsWithoutWildcard
同时既是 Animal
实例的生产者又是消费者。
关键不在于由 List<? extends Animal>
变量引用的内容不能被添加(除了 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<Animal> animalsWithoutWildcard = Stream.of(cats, dogs) //2
.flatMap(Collection::stream)
.collect(Collectors.toList());
and this would be fine also. And then
List<? extends Animal> animals = animalsWithoutWildcard;
would also be fine, because a list of Animal
s 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<? extends Animal>
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's the same list.
animals.add(new Dog());
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论