Java泛型类和通配符

Jer*_*nce 4 java generics wildcard generic-programming

我在java中遇到泛型类的问题.

我有这门课:

public abstract class MyMotherClass<C extends AbstractItem> 
{
    private C   item;

    public void setItem(C item)
    {
        this.item = item;
    }

    public C getItem()
    {
        return item;
    }
}
Run Code Online (Sandbox Code Playgroud)

这个类的实现可以是:

public class MyChildClass extends MyMotherClass<ConcreteItem>
{

}
Run Code Online (Sandbox Code Playgroud)

ConcreteItem只是一个扩展AbstractItem(它是抽象的)的简单类.

所以MyChildClass有一个ConcreteItem,我可以使用:

MyChildClass child = new MyChildClass();
child.setItem(new ConcreteItem());

// automatic cast due to generic class
ConcreteItem item = child.getItem();
Run Code Online (Sandbox Code Playgroud)

好的,目前一切都很好.这是问题所在:

现在我想从集合中提取MyMotherClass的一个实例并设置它的项目(哪个类型未知):

Map<String, MyMotherClass> myCollection = new HashMap<String, MyMotherClass>();
Map<String, AbstractItem> myItems = new HashMap<String, AbstractItem>();

// fill the 2 collections
...


MyMotherClass child = myCollection.get("key");
child.setItem(myItems.get("key2"));
Run Code Online (Sandbox Code Playgroud)

如果我喜欢这样,它会运行.但我有警告,因为MyMotherClass是泛型类型,我不使用泛型类型.但是我不知道我提取的孩子的类型是哪个,所以我想使用通配符:

Map<String, MyMotherClass<?>> myCollection = new HashMap<String, MyMotherClass<?>>();
Map<String, AbstractItem> myItems = new HashMap<String, AbstractItem>();

// fill the 2 collections
...


MyMotherClass<?> child = myCollection.get("key");
child.setItem(myItems.get("key2"));
Run Code Online (Sandbox Code Playgroud)

这就是问题所在:我遇到了一个编译错误:MyMotherClass类型中的方法setItem(capture#1-of?)不适用于参数(AbstractItem)

当我尝试使用继承的通配符时,同样的问题:

Map<String, MyMotherClass<? extends AbstractItem>> myCollection = new HashMap<String, MyMotherClass<? extends AbstractItem>>();
Map<String, AbstractItem> myItems = new HashMap<String, AbstractItem>();

// fill the 2 collections
...


MyMotherClass<? extends AbstractItem> child = myCollection.get("key");
child.setItem(myItems.get("key2"));
Run Code Online (Sandbox Code Playgroud)

我能做什么 ?

谢谢,抱歉我的英语不是很流利;)

Ale*_*yak 6

对于write操作,您需要一个super通配符.

final Map<String, MyMotherClass<? super AbstractItem>> myCollection =
    new HashMap<String, MyMotherClass<? super AbstractItem>>();

final Map<String, AbstractItem> myItems = 
    new HashMap<String, AbstractItem>();

...

final MyMotherClass<? super AbstractItem> child = 
    myCollection.get("key");
child.setItem(myItems.get("key2"));
Run Code Online (Sandbox Code Playgroud)

extends通配符用于read操作.

编辑:回应你的评论.

首先,评估您是否真的需要通配符.

如果答案仍然是肯定的,那么您可以将集合初始化为更具体的类型,然后将它们向下转换为有界通配符,例如

final Map<String, MyChildClass> myInitCollection =
    new HashMap<String, MyChildClass>();

final Map<String, ConcreteItem> myInitItems = 
    new HashMap<String, ConcreteItem>();

myInitCollection.put( "key", new MyChildClass( )  );

final MyMotherClass< ConcreteItem> child = 
    myInitCollection.get("key");

child.setItem(myInitItems.get("key2"));

final Map<String, ? extends MyMotherClass< ? extends AbstractItem >>
    myCollection = myInitCollection;

final Map<String, ? extends AbstractItem> myItems = myInitItems; 
Run Code Online (Sandbox Code Playgroud)

但是请注意,myCollection仍然无法安全地转换为Map<String, MyMotherClass< ? extends AbstractItem>>.

另外,阅读本文关于有界通配符参数,以及何时避免它们.