泛型较低的未绑定vs较高有界通配符 [英] Generic lower unbound vs upper bounded wildcards
问题描述
import java.util.List;
import java.util.ArrayList;
接口Canine {}
类Dog实现Canine {}
公共类Collie延伸Dog {
public static void main(String [] args){
列出<狗> d = new ArrayList< Dog>();
列表< Collie> c = new ArrayList< Collie>();
d.add(new Collie());
c.add(new Collie());
do1(d); DO1(C);
do2(d); DO2(C);
}
static void do1(List< ;? extends Dog> d2){
d2.add(new Collie());
System.out.print(d2.size());
}
static void do2(List< ;? super collie> c2){
c2.add(new Collie());
System.out.print(c2.size());
$ b 这个问题的答案告诉我们,当一个方法需要通配类型典型值,可以访问或修改集合,但不能同时包含两者。 (Kathy and Bert)
当一个方法采用通配类型typ时,这个集合可以被访问或修改,而不是'?
据我所知,
方法do1有 List< ;?延伸狗> d2
因此只能访问d2但不能修改。
方法d2有 List <?超级大牧羊犬> c2
所以c2可以被访问和修改,并且没有编译错误。
解决方案
这个问题的答案告诉我们,当方法采用通配符类型时,可以访问或修改集合,但不能同时采用这两种集合。 (凯西和伯特)
这是一个公平的第一个近似值,但不完全正确。更正确的是:
您只能将null添加到集合<?扩展Dog>
,因为它的add方法的参数是?延伸狗
。无论何时调用一个方法,都必须传递属于声明参数类型的子类型的参数;但对于参数类型?扩展Dog
,如果表达式为 null
,编译器只能确定参数是兼容类型。但是,您当然可以通过调用 clear()
或 remove(Object)
来修改集合。
另一方面,如果您从 Collection <?>中读取数据?超级狗>
,它的迭代器的返回类型是?超级狗
。也就是说,它将返回属于某个未知超类型 Dog
的子类型的对象。但不同的是,Collection可以是 Collection< Object>
,其中只包含 String
的实例。因此,
for(Dog d:collection){...} //不会编译
$ b 所以我们唯一知道的是Object的实例被返回,即迭代这个Collection的唯一类型正确的方法是 p>
for(Object o:collection){...}
但是可以从集合中读取,你只是不知道你会得到什么类型的对象。
<我们可以很容易地将这个观察推广到:给定
class G< T> {...}
和
G< ;?延伸东西> G;
我们只能将null传递给声明类型为 T
,但我们可以调用返回类型为 T
的方法,并为结果指定一个类型为 Something
另一方面,对于
G <?超级东西> G;
我们可以传递任何类型的表达式 Something
到声明类型为 T
的方法参数,我们可以调用返回类型为 T
的方法,但只将结果赋值给一个 Object
类型的变量。
总之,通配符类型的使用限制仅取决于表单的方法声明,而不是方法的作用。
import java.util.List;
import java.util.ArrayList;
interface Canine {}
class Dog implements Canine {}
public class Collie extends Dog {
public static void main(String[] args){
List<Dog> d = new ArrayList<Dog>();
List<Collie> c = new ArrayList<Collie>();
d.add(new Collie());
c.add(new Collie());
do1(d); do1(c);
do2(d); do2(c);
}
static void do1(List<? extends Dog> d2){
d2.add(new Collie());
System.out.print(d2.size());
}
static void do2(List<? super Collie> c2){
c2.add(new Collie());
System.out.print(c2.size());
}
}
The answer for this question tell that when a method takes a wildcard generic typ, the collection can be accessed or modified, but not both. (Kathy and Bert)
What does it mean 'when a method takes a wildcard generic typ, the collection can be accessed or modified, but not both' ?
As far as I know,
The method do1 has List<? extends Dog> d2
so d2 only can be accessed but not modified.
The method d2 has List<? super Collie> c2
so c2 can be accessed and modified and there is no compilation error.
解决方案
The answer for this question tell that when a method takes a wildcard generic typ, the collection can be accessed or modified, but not both. (Kathy and Bert)
That's a fair first approximation, but not quite correct. More correct would be:
You can only add null to a Collection<? extends Dog>
because its add method takes an argument of ? extends Dog
. Whenever you invoke a method, you must pass parameters that are of a subtype of the declared parameter type; but for the parameter type ? extends Dog
, the compiler can only be sure that the argument is of compatible type if the expression is null
. However, you can of course modify the collection by calling clear()
or remove(Object)
.
On the other hand, if you read from a Collection<? super Dog>
, its iterator has return type ? super Dog
. That is, it will return objects that are a subtype of some unknown supertype of Dog
. But differently, the Collection could be a Collection<Object>
containing only instances of String
. Therefore
for (Dog d : collection) { ... } // does not compile
so the only thing we know is that instances of Object are returned, i.e. the only type-correct way of iterating such a Collection is
for (Object o : collection) { ... }
but it is possible to read from a collection, you just don't know what types of objects you will get.
We can easily generalize that observation to: Given
class G<T> { ... }
and
G<? extends Something> g;
we can only pass null to method parameters with declared type T
, but we can invoke methods with return type T
, and assign the result a variable of type Something
.
On the other hand, for
G<? super Something> g;
we can pass any expression of type Something
to method parameters with declared type T
, and we can invoke methods with return type T
, but only assign the result to a variable of type Object
.
To summarize, the restrictions on the use of wildcard types only depend on the form of the method declarations, not on what the methods do.
这篇关于泛型较低的未绑定vs较高有界通配符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!