创建ArrayList的不可变子类

时间:2018-05-05 13:05:21

标签: java arraylist collections immutability

我正在设计一个简单的类子类ArrayList<Color>。在我的应用程序的上下文中,此列表充当“引用列表”,按照循环和明确定义的顺序返回Color个对象。

@SuppressWarnings("serial")
public final class ColorList extends ArrayList<Color> {

    private static int colorIndex = -1;

    public ColorList() {

        this.add(Color.CYAN);
        this.add(Color.DARK_GRAY);
        this.add(Color.GRAY);
        this.add(Color.GREEN);
        this.add(Color.BLUE);
        this.add(Color.LIGHT_GRAY);
        this.add(Color.MAGENTA);
        this.add(Color.ORANGE);
        this.add(Color.PINK);
        this.add(Color.RED);
        this.add(Color.YELLOW);
    }

    //if the color chosen is not the last one, increment index and get it.
    public Color getNextColor() {
        Color color = null;
        if(colorIndex != this.size() - 1) {
            color = this.get(++colorIndex);
        }
        else {
            colorIndex = -1;
            color = this.get(++colorIndex);
        }
        return color;
    }

出于这个原因,我想让这个类不可变,因为它是一个只读列表,其内容是在对象首次实例化时定义的,并在整个生命周期内保持不变。与此同时,我需要能够利用getNextColor()来保持“循环”的方式。

采取的方法

  1. 返回不可修改的引用。

    public static List<Color> getInstance(){
        return Collections.unmodifiableList(new ColorList());
    }
    

    这种方法的问题在于,通过返回List引用它 阻止我调用getNextColor()方法,使我的课无用。 转换并不能解决问题,因为底层对象是一个 UnmodifiableRandomAccessList的实例,无法转换为ColorList

  2. 覆盖涉及可选操作的方法

    @Override
    public boolean add(Color c) {
        throw new UnsupportedOperationException("color list is read-only");
    }
    <overridding other methods such as addAll, remove...>
    

    documentation所述,根据收集的目的,可能会或可能不会实施一些标记为“可选”的操作。但是这会阻止我首先添加颜色,就像在实例化对象时在构造函数中那样。

    我想我可能会误解一些关键的设计方面。有没有办法让我的类不可变,而不限制它(已经是基本的)功能?
    编辑:为了向后兼容,这个类需要显式扩展ArrayList。因此,我不能使用包装类,即使我也认为它是最正确的方法。

2 个答案:

答案 0 :(得分:2)

如果您希望List的内容是不可变的,那么您的构造函数类型有效,只有您应该调用super.add(...)而不是this.add(...);这样做意味着在add(...)上覆盖是明智的,可以保护列表免受外部干扰,同时允许构造函数为您初始化对象。

您的问题不清楚,但如果您还想阻止直接访问列表中的对象,您也可以明显地覆盖get(...)和其他适当的方法 - 但如果您这样做,那么您的{{1} }方法应该调用getNextColor(...)而不是super.get(...)

最后,你可能不需要this.get(...)成员变量,因为你正在扩展一个List - 没有一点在其中嵌套另一个List。

答案 1 :(得分:2)

在你的情况下考虑使用组合而不是继承更好。在这种情况下,您只是不公开可以修改颜色列表的方法。

这是一个示例代码

public final class ColorList {
    private final Color[] values;
    private int colorIndex;

    public ColorList(Color... colors) {
        this.values = colors;
    }

    public Color getNextColor() {
        if (colorIndex == values.length) colorIndex = 0;

        return values[colorIndex++];
    }

    public static void main(String[] args) {
        ColorList cl = new ColorList(Color.CYAN, Color.YELLOW, Color.WHITE);

        System.out.println(cl.getNextColor());
        System.out.println(cl.getNextColor());
        System.out.println(cl.getNextColor());
        System.out.println(cl.getNextColor());
    }
}

您提供的源代码中有一些值得提及的时刻:

  • colorIndex变量是静态的,因此它在ColorList类的所有实例之间共享。如果多一个实例调用getNextColor(),则结果将不一致。也许您有创建Singleton类的想法,因此只能有一个实例?
  • 代码中的颜色列表始终相同且在编译时已知,因此无法使用不同的颜色集创建ColorList实例。这应该是什么?