Java集合协方差问题

假设我们有一个包含这些类的程序:

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

容易,对吗? 好吧,我们现在想要制作一个这样的方法(在一些随机类中):

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

现在的问题是,在具有generics的Java集合中不是协变的(希望这是我正在寻找的术语)并且我不能将ArrayList分配给List 。 我在这里可以看到的唯一解决方案是复制代码并为每种类型执行一个版本,但这显然很糟糕(如果我们有更多的类用不同的列表实现AbstractToolbox会怎么样?)。 哦,显然第二个解决方案是放弃generics并制作一个普通的List,但这是一个好习惯吗?

是否有任何设计模式/实践来解决这些问题?

@Edit:好的,所以我可能不够准确。 我希望所有扩展AbstractToolbox的类都有一个扩展AbstractItem的某些类的List然后我想要一个方法,它将AbstractToolbox作为参数并对其列表中的项执行某些操作(使用将在其中定义的类) AbstractItem所以每个可能列表中的所有项目实际上都有它们)。

您可能需要考虑使用通配符类型进行generics。 这是一个快速链接: 什么是PECS(Producer扩展消费者超级)?

快速回答:将类型更改为List List

为什么你不能分配这个?

想象一下这里的代码……

 List foo = new ArrayList(); foo.add(new BluntItem()); 

静态类型说这应该工作……但你不能这样做! 它会违反ArrayList的类型。 这就是为什么不允许这样做的原因。 如果你改成它

 List foo = new ArrayList(); 

然后,您可以执行分配,但永远不会向列表中添加任何内容。 您仍然可以从列表中检索元素,如AbstractItems。

只是使用List(裸型)一个很好的解决方案?

不,绝对不是:-p

这里有一些额外的想法。 保持一切相同,但使用此:

 interface AbstractToolbox { public List getItems(); } 

这基本上说抽象类的项是未知类型,但子类可以使它具体化。 这将要求您在类型为ExpensiveToolbox或CheapToolbox的引用上调用getItems() ,以便能够检索允许您添加项目的列表等。

 ExpensiveToolbox toolbox = new ExpensiveToolbox(); AbstractToolbox absTB = toolbox; List items1 = absTB.getItems(); //fine List items2 = absTB.getItems(); //compile error List items3= toolbox.getItems(); //fine 

或者,您只需键入AbstractToolbox:

 public interface AbstractToolbox { public List getItems(); } public ExpensiveToolbox implements AbstractToolbox { public List getItems() { //... } 
 public interface AbstractItem { } public class SharpItem implements AbstractItem { } public class BluntItem implements AbstractItem { } public interface AbstractToolbox { public List getItems(); } public class ExpensiveToolbox implements AbstractToolbox { private List items = new ArrayList(); public List getItems() { return this.items; } } public class CheapToolbox implements AbstractToolbox { private List items = new ArrayList(); public List getItems() { return this.items; } } public void doImportantStuff(AbstractToolbox toolbox) { List items = toolbox.getItems(); for(AbstractItem item : items) ... ; }