我想扩展ArrayList,为特定类添加一些方法,这些方法的实例将由扩展的ArrayList保存。简化的说明性代码示例如下。
这对我来说似乎很明智,但我对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);
}
}
}
答案 0 :(得分:8)
该答案中没有任何内容阻止扩展ArrayList;有一个语法问题。存在类扩展,因此我们可以重用代码。
扩展课程的正常反对意见是"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的Effective 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()
,这也会增加计数。
我们可以删除子类的addAll()
,但现在我们依赖于超类实现细节,这可能会改变。我们可以修改我们的addAll()
来迭代并在每个元素上调用add()
,但是现在我们重新实现了超类行为,这违背了目的,如果超类行为依赖于访问权限,则可能并不总是可能的。私人会员。
或者超类可能实现我们的子类没有的新方法,这意味着我们类的用户可能通过直接调用超类方法无意中绕过了预期的行为,因此我们必须跟踪超类API以确定何时,以及是否,子类应该改变。
答案 1 :(得分:0)
我不认为扩展arrayList是必要的。
public class ThingContainer {
private ArrayList<Thing> myThings;
public ThingContainer(){
myThings = new ArrayList<Thing>();
}
public void doSomething(){
//code
}
public Iterator<Thing> getIter(){
return myThings.iterator();
}
}
你应该在你的ThingContainer类中包装ArrayList。然后ThingContainer可以使用您需要的任何处理方法。无需扩展ArrayList;只保留一个私人会员。 希望这会有所帮助。
您可能还想考虑创建一个代表您的Thing Class的界面。这为您提供了更大的可扩展性灵活性。
public Interface ThingInterface {
public void doThing();
}
...
public OneThing implements ThingInterface {
public void doThing(){
//code
}
}
public TwoThing implements ThingInterface {
private String name;
public void doThing(){
//code
}
}
答案 2 :(得分:0)
这是我的建议:
interface ThingStorage extends List<Thing> {
public int total();
}
class ThingContainer implements ThingStorage {
private List<Thing> things = new ArrayList<Thing>();
public boolean add(Thing e) {
return things.add(e);
}
... remove/size/... etc
public int total() {
int tot = 0;
for(int i=0; i < size(); i++) {
tot += ((Thing)get(i)).getAmt();
}
return tot;
}
}
实际上并不需要report()
。 toString()可以完成剩下的工作。