扩展java ArrayList

foo*_*ion 16 java arraylist extend

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

这对我来说似乎很明智,但我对Java很新,我看到其他一些不鼓励扩展ArrayList的问题,例如Extending ArrayList和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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Dav*_*ton 8

该答案中没有任何内容阻止扩展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
}
Run Code Online (Sandbox Code Playgroud)

您不一定需要公开完整的列表界面,只需要集合,或者根本不需要.但是,不暴露任何功能会大大降低一般用途.

在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; }
}
Run Code Online (Sandbox Code Playgroud)

然后我们使用它:

s = new CountingHashSet();
s.addAll(Arrays.asList("bar", "baz", "plugh");
Run Code Online (Sandbox Code Playgroud)

它返回......三个?不.六.为什么?

HashSet.addAll()是实现的HashSet.add(),但这是一个内部实现细节.我们的子类addAll()添加了三个调用super.addAll(),它们调用add(),也会增加计数.

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

或者超类可能实现我们的子类没有的新方法,这意味着我们类的用户可能通过直接调用超类方法无意中绕过了预期的行为,因此我们必须跟踪超类API以确定何时以及是否为子类应该改变.

  • 我喜欢你的最后一句话,"扩展并不总是首选的机制".这就说明了一切.当谈到像这样的东西时,你要说`java.util.List`要编写,如果你仍然希望你的类扩展它,你必须创建一个成员`List`然后制作,其他20个样板方法?所以是的,通常我同意组合比继承更好.但我想在这种情况下继承是要走的路. (3认同)