扩展Java ArrayList


问题内容

我想扩展ArrayList,为特定类添加一些方法,这些类的实例将由扩展的ArrayList保存。下面是一个简化的说明性代码示例。

这对我来说似乎是明智的,但是我对Java还是很陌生,我看到了其他阻碍扩展ArrayList的问题,例如,扩展ArrayList和创建新方法。我对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);
        }
    }
}

问题答案:

该答案中的任何内容均不阻止扩展ArrayList;出现语法问题。存在类扩展,因此我们可以重用代码。

扩展类的通常反对意见是“赞成继承而不是继承”的讨论。扩展并非总是首选的机制,但它取决于您的实际工作。

根据要求编辑组成示例。

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注释执行相同的操作。我不确定Lombok如何公开该接口,或者是否公开。

我在使用glowcoder,在这种情况下,我看不到扩展存在任何根本上的错误-实际上,哪种解决方案更适合该问题是很重要的。

编辑有关继承如何违反封装的详细信息

有关更多详细信息,请参见Bloch的有效Java,第16项。

如果子类依赖于超类行为,并且超类的行为发生变化,则子类可能会中断。如果我们不控制超类,那就不好了。

这是一个具体的示例,它是用伪代码从书中摘录的(对不起,乔什!),并用大量的措辞解释(所有错误都是我的)。

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()添加了三个calls
super.addAll(),它们调用了add(),也增加了count。

我们可以删除子类的addAll(),但是现在我们依赖于超类实现的细节,而细节可能会改变。我们可以修改我们的代码addAll()以迭代并调用add()每个元素,但是现在我们正在重新实现超类行为,这违背了目的,并且如果超类行为依赖于对私有成员的访问,则并非总是可能的。

否则,超类可能会实现我们的子类未实现的新方法,这意味着该类的用户可能会通过直接调用超类方法而无意间跳过预期的行为,因此我们必须跟踪超类API以确定何时以及是否该子类应该改变。