有一个名为Shape的外部接口(名称已更改,但您明白了。)
我有一个具有构造函数的类
Collection<? extends Shape>
本课程正在测试中。我正在写单元测试。我想创建一个扩展Shape的Collection,以提供给构造函数。
我注意到我们项目中没有类实现Shape,所以我写了一个内部类:
private class TestShape implements Shape {
private static final long serialVersionUID = 1L;
@Override
public String getAuthority() {
return "Foo";
}
}
然后我尝试创建我的收藏......
private ArrayList<? extends Shape> shapes;
shapes = new ArrayList<Shape>();
shapes.add(new TestShape());
然而,Eclipse对我不满意......
The method add(capture#1-of ? extends Shape) in the type ArrayList<capture#1-of ? extends Shape> is not applicable for the arguments (Test.TestShape)
为什么呢?
编辑:大量编辑,因为泛型语法使得SO不快乐
答案 0 :(得分:4)
我们假设我们TestShape
实现了Shape
,而EvilShape
也实现了Shape
。现在想象如下:
private ArrayList<? extends Shape> shapes; //OK
shapes = new ArrayList<EvilShape>(); // OK
authorities.add(new TestShape()); // Type safety issue
显然,这样的事情是行不通的。相反,请执行以下操作:
private ArrayList<Shape> shapes; //OK
shapes = new ArrayList<Shape>(); // OK
authorities.add(new TestShape()); // OK
或以下内容:
private ArrayList<? super Shape> shapes; //OK
shapes = new ArrayList<Shape>(); // Or, for that matter, an `ArrayList<Object>`.
authorities.add(new TestShape()); // OK
这将有效,因为您可以将Shape
的子类/实现添加到List<Shape>
。使用精确类型参数而不是通配符将阻止以下操作:
private ArrayList<Shape> shapes; //OK
shapes = new ArrayList<EvilShape>(); // DANGER! Java does not allow this
缩写 PECS 有助于记住这一点:Producer Extends,Consumer Super。这来自Joshua Bloch的 Effective Java 。
答案 1 :(得分:0)
我有一个具有构造函数的类
Collection<? extends Shape>
...
然后我尝试创建我的收藏......
private ArrayList<? extends Shape> shapes;
我认为可能令人困惑的一件事是,因为有一个类似的方法参数(Collection<? extends Shape>
),你不需要像这样创建一个变量。想想使用通配符泛型(?extends X或?super Y)的变量/参数的用途,作为编写一些“通用”代码的方法,该代码可以使用来自family of similar types
而不是通常的任何值。来自特定类型的值“。
-
这方面的完美示例是Collection.copy()的签名:
public static <T> void copy(List<? super T> dest,List<? extends T> src)
此方法允许我传递List<Integer>
的实例,并将其值复制到List<Number>
的实例中。它还允许我将List<Integer>
复制到List<Integer>
。它允许我使用不同类型的相同代码(“类型族”)。我可以使用通配符泛型变量来测试它:
List<Integer> src = new ArrayList<>();
src.add(1);
List<Number> dest = new ArrayList<>();
Collection.copy(src, dest);
assert(src.get(0), dest.get(0));
-
使用List<? super T>
或List<? extends T>
等变量或参数的重大权衡因为它不是特定类型的变量,因此存在限制您可以对它所指向的实例有什么假设,因此您可以用它来做什么。
PECS
只是过于简单化,但是它有一个很好的助记符:PECS
=“生产者扩展,消费者超级”意味着通用通配符类型的变量(或参数)用extends
只能用于“生成”值,即你只能安全地读取它,你不能添加它。类似地,使用super
的通用通配符类型的变量只能用于“消耗”值 - 您可以安全地仅为其提供值(添加),但是对于可以假设的值,存在限制可以从中“读”出来。
因此,对于您的示例,我会避免使用带有通配符泛型的变量,只需传入不同的List&lt;&gt;构造函数的类型。如果必须使用带通配符通用的变量,那么使用特定类型的变量来填充它,因为使用extends
的通配符通用的变量不能用于填充它:
private ArrayList<Shape> shapesSpecific;
private ArrayList<? extends Shape> shapesExtends;
shapesExtends = shapesSpecific = new ArrayList<Shape>();
shapesSpecific.add(new TestShape()); // fine
//shapesExtends.add(new TestShape()); // error - but the line above added a value already
// Another option - create it already filled:
// shapeExtends = Arrays.asList(new TestShape());
new Foo(shapesSpecific); // fine
new Foo(shapesExtends); // fine
-
您可能会发现此问题/答案很有用:
How can I add to List<? extends Number> data structures? - 这是一个例子:
List<? extends Number> foo3
的通配符声明意味着 变量foo3
可以保存一系列类型的任何值(相反 比任何特定类型的值)。这意味着其中任何一个都是 法律任务:List<? extends Number> foo3 = new ArrayList<Number>; // Number "extends" Number List<? extends Number> foo3 = new ArrayList<Integer>; // Integer extends Number List<? extends Number> foo3 = new ArrayList<Double>; // Double extends Number
因此,鉴于此,您可以向
List foo3
添加哪种类型的对象 在上述任何可能的ArrayList
之后是合法的 分配:
- 您无法添加
Integer
,因为foo3
可能指向List<Double>
。- 您无法添加
Double
,因为foo3
可能指向List<Integer>
。- 您无法添加
Number
,因为foo3
可能指向List<Integer>
。您无法向
List<? extends T>
添加任何对象,因为您无法保证它真正指向的是哪种List
,所以您不能 保证List
中允许该对象。唯一的 “保证”是你只能阅读它,你会得到T
或。{T
的子类。
这是一个类似的问题/答案:
答案 2 :(得分:0)
如果是单元测试,只需创建一个Shapes集合:
import java.util.ArrayList;
import java.util.Collection;
public class test {
public interface Shape {
String getAuthority();
}
private static class TestShape implements Shape {
@Override
public String getAuthority() {
return "Foo";
}
}
public static void method(Collection<? extends Shape> shapes){
}
public static void main(String ... args){
ArrayList<Shape> shapes;
shapes = new ArrayList<Shape>();
shapes.add(new TestShape());
method(shapes);
}
}
这可以干净地编译,很可能是在实际操作中如何使用该方法。