Java中的常量数组

Java中的常量数组,java,arrays,Java,Arrays,我有一个Move类,它是不可变的(基本上只有两个整数) I have类使用常量移动: public class Moves { public static final Move NW = Move.make(-1, -1); public static final Move N = Move.make(0, -1); public static final Move NE = Move.make(1, -1); public static final Move

我有一个
Move
类,它是不可变的(基本上只有两个整数)

I have类
使用常量移动

public class Moves {

    public static final Move NW = Move.make(-1, -1);
    public static final Move N = Move.make(0, -1);
    public static final Move NE = Move.make(1, -1);
    public static final Move E = Move.make(1, 0);
    public static final Move SE = Move.make(1, 1);
    public static final Move S = Move.make(0, 1);
    public static final Move SW = Move.make(-1, 1);
    public static final Move W = Move.make(-1, 0);

    public final static Move[] ALL = {
        NW, N, NE, E, SE, S, SW, W
    };

    public final static Move[] CARDINAL = {
        N, E, S, W
    };
}
我需要用于以下方法的数组:

public void walkToRandomSide(Move[] possibleMoves)
它工作得很好,但是很明显,
ALL
CARDINAL
的元素可以由任何人随时更改。当然,我知道这些元素不可能是最终的

这很糟糕——我希望它成为库的一部分,并且尽可能稳定和可预测

我该如何解决这个问题?
创建一个每次创建一个新数组的方法?

数组始终是可修改的(注释中所述的零大小数组除外),您应该在此处使用
集合。不可修改列表。例如:

public final static List<Move> ALL =
        Collections.unmodifiableList(Arrays.asList(NW, N, NE, E, SE, S, SW, W));
public最终静态列表所有=
集合.不可修改的列表(数组.asList(NW,N,NE,E,SE,S,SW,W));

数组始终是可修改的(注释中所述的零大小数组除外),您应该在此处使用
集合。不可修改列表。例如:

public final static List<Move> ALL =
        Collections.unmodifiableList(Arrays.asList(NW, N, NE, E, SE, S, SW, W));
public最终静态列表所有=
集合.不可修改的列表(数组.asList(NW,N,NE,E,SE,S,SW,W));

Java。在大多数其他语言中,它们的功能远远超过枚举,它们是
final
类,具有一组特定的
final
成员,这些成员是该类的唯一实例。如果您使用的是Java1.4或更高版本,则它们不可用,但这正是您所需要的。

Java。在大多数其他语言中,它们的功能远远超过枚举,它们是
final
类,具有一组特定的
final
成员,这些成员是该类的唯一实例。如果您使用的是Java 1.4或更高版本,则它们不可用,但这正是您想要的。

不,请避免使用数组。数组总是可变的,即使您每次创建一个新数组,这意味着您的调用者在传递集合时必须执行防御复制

首先,我要按照Steve的建议,将您的
Move
类更改为enum。然后任何人都可以使用
EnumSet.allOf

其次,对于
CARDINAL
,我要么使用
Collections.unmodifiableList
来包装您在内部创建的列表(并且知道您永远不会修改自己),要么使用一个明确谈论不可变性的类型,例如from(或者可能是
ImmutableSet
,这取决于排序是否重要)


使用单独类型的好处是,您可以向调用者明确它是不可变的,而不仅仅是记录返回的列表恰好是不可变的。虽然通常使用接口作为返回类型是一个好主意,但在这里值得在类型系统中传达一个非常具体的保证-一个gu调用方也可以在其代码中使用的数组。

不,只需避免使用数组。数组始终是可变的,即使您每次创建一个新数组,这意味着您的调用方在传递集合时也必须执行防御副本

首先,我会按照Steve的建议将您的
Move
类更改为enum。然后任何人都可以使用
EnumSet.allOf

其次,对于
CARDINAL
,我要么使用
Collections.unmodifiableList
来包装您在内部创建的列表(并且知道您永远不会修改自己),要么使用一个明确谈论不可变性的类型,例如from(或者可能是
ImmutableSet
,这取决于排序是否重要)


使用单独类型的好处是,您可以向调用者明确它是不可变的,而不仅仅是记录返回的列表恰好是不可变的。虽然通常使用接口作为返回类型是一个好主意,但在这里值得在类型系统中传达一个非常具体的保证-一个gu调用方也可以在其代码中使用的数组。

在get()方法中创建数组的防御副本,以便任何人都不能更改您的数组:

public Move[] getAll{
    Move[] tmpArray = new Move[ALL.length];
    for (int i = 0; i < All.length; i++) {
        tmpArray[i] = All[i];
    }
    return tmpArray;
}
public Move[]getAll{
Move[]tmpArray=新移动[ALL.length];
for(int i=0;i
或者更好

public Move[] getAll{
    Move[] tmpArray = new Move[ALL.length];
    for (int i = 0; i < All.length; i++) {
        tmpArray[i] = new Move(All[i]); // make sure to create a deep copy using copy constructor
    }
    return tmpArray;
}
public Move[]getAll{
Move[]tmpArray=新移动[ALL.length];
for(int i=0;i
在get()方法中创建阵列的防御副本,以便任何人都无法更改您的阵列:

public Move[] getAll{
    Move[] tmpArray = new Move[ALL.length];
    for (int i = 0; i < All.length; i++) {
        tmpArray[i] = All[i];
    }
    return tmpArray;
}
public Move[]getAll{
Move[]tmpArray=新移动[ALL.length];
for(int i=0;i
或者更好

public Move[] getAll{
    Move[] tmpArray = new Move[ALL.length];
    for (int i = 0; i < All.length; i++) {
        tmpArray[i] = new Move(All[i]); // make sure to create a deep copy using copy constructor
    }
    return tmpArray;
}
public Move[]getAll{
Move[]tmpArray=新移动[ALL.length];
for(int i=0;i
为什么不将常量定义为enum?使用私有构造函数创建一个enum!我想在循环中对它进行迭代,并且我需要基数和全部(+对角线,我在这里没有提到).Enum不能这样做。请参见JonSkeet关于
EnumSet
的回答。为什么不将常量定义为Enum?使用私有构造函数创建一个Enum!我想在循环中对其进行迭代,并且我需要基数和全部(+对角线,我在这里没有提到).Enum不能这样做。请参阅JonSkeet关于
EnumSet
的回答。好吧,我不能使用Enum-可能有人还想添加Move.make(2,1)等移动对于horse,它可以扩展到移动之外。但是不可变列表看起来可能会起作用。好吧,使用
集合。不可修改列表
,因为我不想再向我的项目中添加另一个jar。嗯,我不能使用enum-可能有人还想添加移动,例如Move.make(2,1)for hors