[英]java generics: bounded wildcard upsetting eclipse
There is an external interface named Shape (name changed, but you get the idea). 有一个名为Shape的外部接口(名称已更改,但是您知道了)。
I have a class that has a constructor that takes 我有一个带有需要的构造函数的类
Collection<? extends Shape>
This class is under test. 该课程正在测试中。 I am writing the unit tests.
我正在编写单元测试。 I want to create a Collection that extends Shape to provide to the constructor.
我想创建一个扩展Shape的Collection,以提供给构造函数。
I notice that no class in our project implements Shape, so I write an inner class: 我注意到我们项目中没有任何类实现Shape,所以我写了一个内部类:
private class TestShape implements Shape {
private static final long serialVersionUID = 1L;
@Override
public String getAuthority() {
return "Foo";
}
}
Then I try to create my Collection ... 然后,我尝试创建我的收藏集...
private ArrayList<? extends Shape> shapes;
shapes = new ArrayList<Shape>();
shapes.add(new TestShape());
Eclipse however is unhappy with me ... 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)
Why? 为什么?
edit: Lots of editing because generics syntax makes SO unhappy 编辑:大量编辑,因为泛型语法使SO不满意
Let's imagine that we have TestShape
implements Shape
, and EvilShape
also implements Shape
. 假设我们有
TestShape
实现Shape
,而EvilShape
也实现Shape
。 Now imagine the following: 现在想象一下:
private ArrayList<? extends Shape> shapes; //OK
shapes = new ArrayList<EvilShape>(); // OK
authorities.add(new TestShape()); // Type safety issue
Clearly, such a thing won't work. 显然,这样的事情是行不通的。 Instead, do the following:
而是,请执行以下操作:
private ArrayList<Shape> shapes; //OK
shapes = new ArrayList<Shape>(); // OK
authorities.add(new TestShape()); // OK
Or the following: 或以下内容:
private ArrayList<? super Shape> shapes; //OK
shapes = new ArrayList<Shape>(); // Or, for that matter, an `ArrayList<Object>`.
authorities.add(new TestShape()); // OK
This will work, as you can add a subclass/implementation of Shape
to a List<Shape>
. 这将起作用,因为您可以将
Shape
的子类/实现添加到List<Shape>
。 Using an exact type parameter and not a wildcard will prevent the following: 使用确切的类型参数而不使用通配符将防止以下情况:
private ArrayList<Shape> shapes; //OK
shapes = new ArrayList<EvilShape>(); // DANGER! Java does not allow this
The abbreviation PECS helps remember this: Producer Extends, Consumer Super. PECS的缩写有助于记住这一点:生产者扩展,超级消费者。 This comes from Joshua Bloch's Effective Java .
这来自Joshua Bloch的Effective Java 。
I have a class that has a constructor that takes
我有一个带有需要的构造函数的类
Collection<? extends Shape>
...
...
Then I try to create my Collection ...
然后,我尝试创建我的收藏集...
private ArrayList<? extends Shape> shapes;
I think one thing that may be confusing is that just because there is a method parameter like that ( Collection<? extends Shape>
), you don't need to create a variable like that. 我认为可能会引起混淆的一件事是,仅仅因为有一个这样的方法参数(
Collection<? extends Shape>
),您不需要创建这样的变量。 Think of the purpose of a variable/parameter that uses wildcard generics (? extends X or ? super Y) as a way to write some "generic" code that will work with any value from a family of similar types
rather than the usual "a value from a specific type". 想一想使用通配符泛型(?扩展X或?super Y)作为变量/参数的目的的一种方式,该方式可以编写一些“泛型”代码,该代码将与
family of similar types
的family of similar types
任何值一起工作,而不是通常的“ a”特定类型的值”。
-- -
The perfect example of this is the signature for Collection.copy(): 完美的例子是Collection.copy()的签名:
public static <T> void copy(List<? super T> dest,List<? extends T> src)
This method allows me to pass an instance of List<Integer>
and have its values copied into an instance of List<Number>
. 此方法允许我传递
List<Integer>
的实例,并将其值复制到List<Number>
的实例中。 It also allows me to copy List<Integer>
into List<Integer>
. 它还允许我将
List<Integer>
复制到List<Integer>
。 It lets me use the same code with different types ("family of types"). 它使我可以将相同的代码用于不同的类型(“类型族”)。 I can test it with using wildcard generic variables at all:
我可以使用通配符泛型变量进行测试:
List<Integer> src = new ArrayList<>();
src.add(1);
List<Number> dest = new ArrayList<>();
Collection.copy(src, dest);
assert(src.get(0), dest.get(0));
-- -
The big tradeoff with using a variable or parameter like List<? super T>
使用诸如
List<? super T>
List<? super T>
or List<? extends T>
List<? super T>
或List<? extends T>
List<? extends T>
is that because its not a variable for a specific type, there are limitations on what you can assume about the instance it points to and therefore you are limited in what you can do with it. List<? extends T>
在于,由于它不是特定类型的变量,因此您对它所指向的实例的假设有一定的限制,因此您只能使用它进行操作。
PECS
is a tiny bit of an oversimplification, but it makes a great mnemonic: PECS
= "producer extends, consumer super" means that a variable (or parameter) for a generic wildcard type that uses extends
can only be used to "produce" values, ie you can safely only read from it, you can't add to it. PECS
有点过分简化,但它有一个很大的助记符: PECS
=“生产者扩展,消费者超级”表示使用extends
的通用通配符类型的变量(或参数)只能用于“产生”值,即您只能安全地读取它,而不能添加它。 Similarly, a variable for a generic wildcard type that uses super
can only be used to "consume" values - you can safely only feed it values (add), but there are limitations on what you can assume about the values that could be "read" from it. 同样,使用
super
的通用通配符类型的变量只能用于“消费”值-您可以安全地仅将其值(添加)提供给它,但是对于可以“读取”的值的假设存在限制“ 从中。
So, for your example, I would avoid using a variable with a wildcard generic at all, just pass in different List<> types to the constructor. 因此,对于您的示例,我完全避免使用带有通配符泛型的变量,只需将不同的List <>类型传递给构造函数。 If you must use a variable with a wildcard generic, that use a variable to the specific type to fill it because the variable with the wildcard generic that uses
extends
can't be used to fill it: 如果必须使用带有通配符泛型的变量,则使用特定类型的变量来填充它,因为带有
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
-- -
You might find this question/answer useful: 您可能会发现此问题/答案很有用:
How can I add to List<? 如何添加到列表<? extends Number> data structures?
扩展Number>数据结构? - here's an example from it:
-这是一个例子:
The wildcard declaration of
List<? extends Number> foo3
List<? extends Number> foo3
的通配符声明List<? extends Number> foo3
means that the variablefoo3
can hold any value from a family of types (rather than any value of a specific type).List<? extends Number> foo3
意味着变量foo3
可以保存一系列类型中的任何值(而不是特定类型的任何值)。 It means that any of these are legal assignments:这意味着这些都是合法的转让:
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
So, given this, what type of object could you add to
List foo3
that would be legal after any of the above possibleArrayList
assignments:因此,鉴于此,在上述任何可能的
ArrayList
分配之后,您可以将什么类型的对象添加到List foo3
,这是合法的:
- You can't add an
Integer
becausefoo3
could be pointing at aList<Double>
.您不能添加
Integer
因为foo3
可能指向List<Double>
。- You can't add a
Double
becausefoo3
could be pointing at aList<Integer>
.您不能添加
Double
因为foo3
可能指向List<Integer>
。- You can't add a
Number
becausefoo3
could be pointing at aList<Integer>
.您不能添加
Number
因为foo3
可能指向List<Integer>
。You can't add any object to
List<? extends T>
您不能将任何对象添加到
List<? extends T>
List<? extends T>
because you can't guarantee what kind ofList
it is really pointing to, so you can't guarantee that the object is allowed in thatList
.List<? extends T>
因为您不能保证它真正指向的是哪种List
,因此不能保证该List
允许该对象。 The only "guarantee" is that you can only read from it and you'll get aT
or subclass ofT
.唯一的“保证”是,你只能从它读,你会得到一个
T
的或子类T
。
This is a similar question/answer: 这是一个类似的问题/答案:
Difference between <? 之间的区别? super T> and <?
超级T>和<? extends T> in Java
在Java中扩展T>
If this is for a unit test, just make a collection of Shapes: 如果这是用于单元测试,则只需收集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);
}
}
this compiles cleanly and is likely how the method is used in actual practice. 这样可以编译得很干净,并且很有可能在实际实践中使用该方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.