简体   繁体   English

Java集合协方差问题

[英]Java collections covariance problem

Lets say we have a program which contains such classes: 假设我们有一个包含这些类的程序:

public interface AbstractItem {
}
public SharpItem implements AbstractItem {
}
public BluntItem implements AbstractItem {
}

public interface AbstractToolbox {
    //well the problem starts here...
    public List<AbstractItem> getItems();
}
public ExpensiveToolbox implements AbstractToolbox {
    private List<SharpItem> items = new ArrayList()<SharpItems>;
    public List<SharpItem> getItems() { return this.items; }
}
public CheapTooblox implements AbstractToolbox {
    private List<BluntItem> items = new ArrayList()<BluntItem>;
    public List<BluntItem> getItems() { return this.items; }
}

Easy, right? 容易,对吗? Well lets say we now want to make a method like this (in some random class): 好吧,我们现在想要制作一个这样的方法(在一些随机类中):

public void doImportantStuff(AbstractToolbox toolbox) {
//important stuff!
//this obviously won't work
    List<AbstractToolbox> items = toolbox.getItems();
//do some stuffwith all items
}

Now the problem is that in Java collections with generics aren't covariant (hope that's the term I'm looking for) and I can't assign an ArrayList<ExpensiveToolbox> to a List<AbstractToolbox> . 现在的问题是,在具有泛型的Java集合中不是协变的(希望这是我正在寻找的术语)并且我不能将ArrayList<ExpensiveToolbox>分配给List<AbstractToolbox> The only solution I can see here is to duplicate the code and do a version for each type, but that would obviously suck (what if we had more classes implementing AbstractToolbox with different lists?). 我在这里可以看到的唯一解决方案是复制代码并为每种类型执行一个版本,但这显然很糟糕(如果我们有更多的类用不同的列表实现AbstractToolbox会怎么样?)。 Oh obviously the second solution would be to drop the generics and make a normal List, but is it a good practice? 哦,显然第二个解决方案是放弃泛型并制作一个普通的List,但这是一个好习惯吗?

Are there any design pattern/practices to tackle such problems? 是否有任何设计模式/实践来解决这些问题?

@Edit: ok so I might not be precise enough. @Edit:好的,所以我可能不够准确。 I want all the classes which extend AbstractToolbox to have a List of certain classes which extend AbstractItem and then I want a method that will take an AbstractToolbox as a parameter and do something on the items in its list (using the classes that would be defined in AbstractItem so all the items of every possible list would actually have them). 我希望扩展AbstractToolbox的所有类都有一个扩展AbstractItem的某些类的List然后我想要一个方法,它将AbstractToolbox作为参数并对其列表中的项执行某些操作(使用将在其中定义的类) AbstractItem所以每个可能列表中的所有项目实际上都有它们)。

You're probably going to need to take a look at using wildcard types for generics. 您可能需要查看使用通配符类型进行泛型。 Here's a quick link: What is PECS (Producer Extends Consumer Super)? 这是一个快速链接: 什么是PECS(生产者扩展消费者超级)?

Quick answer: change the type to List<? extends AbstractItem> 快速回答:将类型更改为List<? extends AbstractItem> List<? extends AbstractItem>

Why can't you just assign this? 你为什么不能分配这个?

Imagine the code here... 想象一下这里的代码......

List<AbstractItem> foo = new ArrayList<SharpItem>();
foo.add(new BluntItem());

The static typing says this should work... but you can't do that! 静态类型说这应该工作......但你不能这样做! It would violate the ArrayList's type. 它会违反ArrayList的类型。 That's why this is disallowed. 这就是为什么不允许这样做的原因。 If you change it to 如果你改成它

List<? extends AbstractItem> foo = new ArrayList<SharpItem>();

you can then do the assignment, but never add anything to the list. 然后,您可以执行分配,但永远不会向列表中添加任何内容。 You can still retrieve elements from the list, however, as AbstractItems. 您仍然可以从列表中检索元素,如AbstractItems。

Is just using List (bare type) a good solution? 只是使用List(裸型)一个很好的解决方案?

No, definitely not :-p 不,绝对不是:-p

Here are a couple of extra ideas. 这里有一些额外的想法。 Leave everything the same, but use this: 保持一切相同,但使用此:

interface AbstractToolbox {
    public List<? extends AbstractItem> getItems();
}

This basically says that the abstract class' items are an unknown type, but subclasses can make it concrete. 这基本上说抽象类的项是未知类型,但是子类可以使它具体化。 This would require you to call getItems() on a reference of type ExpensiveToolbox or CheapToolbox to be able to retrieve a list that allows you to add items, etc. 这将要求您在类型为ExpensiveToolbox或CheapToolbox的引用上调用getItems() ,以便能够检索允许您添加项目的列表等。

ExpensiveToolbox toolbox = new ExpensiveToolbox();
AbstractToolbox absTB = toolbox;

List<? extends AbstractItem> items1 = absTB.getItems(); //fine
List<SharpItem> items2 = absTB.getItems(); //compile error
List<SharpItem> items3= toolbox.getItems(); //fine

Alternatively, you could just type AbstractToolbox: 或者,您只需键入AbstractToolbox:

public interface AbstractToolbox<T extends AbstractItem> {
    public List<T> getItems();
}
public ExpensiveToolbox implements AbstractToolbox<SharpItem> {
    public List<SharpItem> getItems() { //...
}
public interface AbstractItem
{
}
public class SharpItem implements AbstractItem
{
}
public class BluntItem implements AbstractItem
{
}

public interface AbstractToolbox<T extends AbstractItem>
{
    public List<T> getItems();
}
public class ExpensiveToolbox implements AbstractToolbox<SharpItem>
{
    private List<SharpItem> items = new ArrayList<SharpItem>();
    public List<SharpItem> getItems() { return this.items; }
}
public class CheapToolbox implements AbstractToolbox<BluntItem>
{
    private List<BluntItem> items = new ArrayList<BluntItem>();
    public List<BluntItem> getItems() { return this.items; }
}


public void doImportantStuff(AbstractToolbox<?> toolbox)
{
    List<? extends AbstractItem> items = toolbox.getItems();

    for(AbstractItem item : items) 
        ... ;

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM