这对我来说似乎是明智的,但我对Java非常新鲜,我看到其他一些不鼓励扩展ArrayList的问题,例如Extending ArrayList and Creating new methods.我不知道有足够的Java来理解异议.
在我以前的尝试中,我最终在ThingContainer中创建了一些基本上传递给ArrayList的方法,因此扩展似乎更容易.
有没有更好的方法来做我想要做的事情?如果是这样,应该如何实施?
import java.util.*; class Thing { public String name; public int amt; public Thing(String name,int amt) { this.name = name; this.amt = amt; } public String toString() { return String.format("%s: %d",name,amt); } public int getAmt() { return amt; } } class ThingContainer extends ArrayList<Thing> { public void report() { for(int i=0; i < size(); i++) { System.out.println(get(i)); } } public int total() { int tot = 0; for(int i=0; i < size(); i++) { tot += ((Thing)get(i)).getAmt(); } return tot; } } public class Tester { public static void main(String[] args) { ThingContainer blue = new ThingContainer(); Thing a = new Thing("A",2); Thing b = new Thing("B",4); blue.add(a); blue.add(b); blue.report(); System.out.println(blue.total()); for (Thing tc: blue) { System.out.println(tc); } } }
解决方法
正常的反对扩展一个类是“favor composition over inheritance”讨论.扩展并不总是首选的机制,但这取决于你在做什么.
根据请求编辑组合示例.
public class ThingContainer implements List<Thing> { // Or Collection based on your needs. List<Thing> things; public boolean add(Thing thing) { things.add(thing); } public void clear() { things.clear(); } public Iterator<Thing> iterator() { things.iterator(); } // Etc.,and create the list in the constructor }
您不一定需要公开一个完整的列表界面,只是收集,或根本没有.尽管没有显示功能大大降低了一般的有用性.
在Groovy中,您可以使用@Delegate注释自动构建方法. Java可以使用Project Lombok的@Delegate注释来做同样的事情.我不知道龙目岛如何暴露界面,或者如果它.
我用glowcoder,在这种情况下,我没有看到任何扩展的根本错误 – 这真的是一个解决问题的问题.
编辑关于继承如何违反封装的细节
有关详细信息,请参阅Bloch的有效Java,第16项.
如果一个子类依赖于超类行为,并且超类的行为发生变化,则子类可能会中断.如果我们不控制超类,这可能是坏的.
这是一个具体的例子,从书中解除(对不起Josh!),伪代码,并大量释义(所有的错误都是我的).
class CountingHashSet extends HashSet { private int count = 0; boolean add(Object o) { count++; return super.add(o); } boolean addAll(Collection c) { count += c.size(); return super.addAll(c); } int getCount() { return count; } }
然后我们用它:
s = new CountingHashSet(); s.addAll(Arrays.asList("bar","baz","plugh");
它返回…三?不.六.为什么?
HashSet.addAll()在HashSet.add()上实现,但这是一个内部实现细节.我们的子类addAll()添加三个,调用super.addAll(),它调用add(),它也增加count.
我们可以删除子类的addAll(),但现在我们依赖于超类实现细节,这可能会改变.我们可以修改我们的addAll()来迭代并在每个元素上调用add(),但是现在我们正在重新实现超类行为,如果超类行为取决于对私有成员的访问,那么它们的目的就会失败,而且可能并不总是可能的.
或者一个超类可以实现一个新的方法,我们的子类不会,这意味着我们的类的用户可能无意中通过直接调用超类方法绕过预期的行为,所以我们必须跟踪超类API来确定什么时候,如果是子类应改变.