我应该如何为类的每个实例声明一个常量集?

时间:2010-03-10 15:02:38

标签: java oop static set final

我希望在我的班级中有一个常量集,对于班级的所有实例都可以看到。

首先,我不知道是否需要将其声明为“静态”。据我所知,其他实例将看到静态字段的任何更改(由其中一个实例完成)(因此静态变量未绑定到特定实例)。而且,静态字段可以在不使用任何实例的情况下进行更改(我们直接使用该类)。因此,静态字段的所有这些特殊属性都与它的更改方式以及这些更改的影响有关。但在我的情况下,我希望有一个常数(所以“变化”问题在这里不相关)。所以,我可能不需要使用“静态”。正确?

其次,我的集合将包含很多元素,我不想一次定义集合的值(当我创建变量时)。换句话说,我想声明一个集合,然后逐步向该集合添加元素。但是,如果我使用常量,我就无法做到。是否可以指定集合的​​值,然后使其保持不变?

第三,我已经意识到,如果我尝试在任何方法之外更改变量值,可能会出现一些问题。那么,它是如何运作的?

增加:

行。由于答案,我理解它应该是“final”和“static”(因为它是一个常量集,并且它不会与任何特定实例相关联,它应该对该类的所有实例都可见)。但是我还是有问题。我想使用“add”来指定集合,如果它是常量(最终),我就无法添加到集合中。而且,我无法改变方法之外的变量值(为什么?)。无论如何,我并不坚持使用“添加”来定义集合。我准备立即定义它。但我不知道该怎么做。我尝试过这样的事情:

final static Set allowedParameters = new HashSet("aaa","bbb");
final static Set allowedParameters = new HashSet(["aaa","bbb"]);
final static Set allowedParameters = new HashSet({"aaa","bbb"});
final static Set allowedParameters = new HashSet(Arrays.asList({"userName"}));

他们没有工作。

已添加2:

任何人都可以解释一下,请求Tadeusz Kopec提供的代码吗?

class YourClass {
    private static void fillSet(Set<SomeType> set) {
        // here you add elements, like
        set.add(new SomeType());
    }
    private final static Set<SomeType> yourSetField;
    static {
        final Set<SomeType> tempSet = new HashSet<SomeType>();
        fillSet(tempSet);
        yourSetField = Collection.unmodifiableSet(tempSet);
    }
}

1
fillSet方法有一个名为“set”的变量。为什么不在方法中使用?
2。 SomeType()中的fillSet是什么?这种方法有什么作用?
3。 fillSet做了什么?稍后在示例中,我们为此方法提供一个空集。做什么的?
4。我们如何宣布tempSet为最终版?
5。究竟unmodifiableSet到底是做什么的?根据名称,它生成了一个无法修改的集合,我想。但为什么将yourSetField声明为final是不够的呢?比它也不变。

4 个答案:

答案 0 :(得分:6)

您想要在您的设置中添加一次元素,然后只读取其内容,或者您​​是否希望能够随时添加元素?如果您创建一次,请执行以下操作:

class YourClass {
    private static void fillSet(Set<SomeType> set) {
        // here you add elements, like
        set.add(new SomeType());
    }
    private final static Set<SomeType> yourSetField;
    static {
        final Set<SomeType> tempSet = new HashSet<SomeType>();
        fillSet(tempSet);
        yourSetField = Collections.unmodifiableSet(tempSet);
    }
}

现在 - 它是私有的,所以你班外的人都无法访问它。而且它不可修改,所以没有人可以改变它的内容。

如果你想随时添加元素,你就会遇到并发问题 - 请阅读extraneon的答案。

修改
根据要求,我解释了这段代码的作用。

首先 - 神秘&lt;&gt;括号: 我假设您使用的是Java 1.5或更高版本并使用了generics。简而言之 - 如果您声明一个List类型的变量,它将保存对象。如果您希望将字符串保留在其中,则必须在从列表中检索字符串时将其强制转换。实施例

List myList = new ArrayList();
myList.add("Hello, my Jon Skeet Number decreases");
String firstElement = (String) myList.get(0);

需要使用强制转换为String。此外,没有什么可以阻止您将BigDecimal添加到myList。但是如果你检索它并尝试强制转换为String,则会得到ClassCastException。

myList.add(0, BigDecimal.ZERO); // perfectly legal
String anotherString = (String) myList.get(0); // compiles, but ClassCastException at runtime

所以Java 1.5引入了泛型。您可以指定,List只能包含字符串。语法使用&lt;&gt;括号:

List<String> myList = new ArrayList<String>();
myList.add("Hi everybody");
String firstElem = myList.get(0); // no cast required
myList.add(BigDecimal.ZERO); // compiler error: cannot cast BigDecimal to String

同样适用于其他集合,例如Sets。我写了关于列表的内容,因为从列表中检索更方便。我在我的示例中使用了SomeType,因为我不知道你想要在你的Set中保留什么。您应该将其替换为您要存储的对象类型。

现在 - 静态阻止。有两种方法可以直接在声明中初始化静态字段:

static private int instanceCount = 0;

如果初始值是一个简单的表达式,这很有用 或者在静态初始化块

static {
    // some code, that can use class static variables, class static methods, declare its own variables etc.
}

如果某些静态字段的初始值需要更复杂的计算,这很有用。

现在您的问题

  1. 使用set的参数fillSet。添加了一个元素:set.add(new SomeType());
  2. 由于我不知道您要保留在集合中的内容,因此我将其元素的类型命名为SomeType。它将替换为您要使用的类型。 new SomeType();创建一个(假设的)SomeType实例,调用其无参数构造函数。
  3. fillSet完全符合其名称的含义 - 接受一个集合并填充它(为它添加一些值)。我们给它一个空集,以便我们得到一个元素fillSet的集合。 fillSet是您应该放置初始化集合的所有代码的地方。将它分开是件好事。
  4. tempSet是静态初始化块中的局部变量,它被分配一次并且从不重新分配。为了表达这一点,我宣布它是最终的。这是我使用findBugs时的习惯,我认为这对可读性有好处。
  5. 使yourSetField成为最终意味着您无法在初始化后编写yourSetField = new HashSet<SomeType>()。但是,任何可以访问yourSetField的人都可以写yourSetField.add(...)。如果yourSetField是不可修改的集合,则添加到它将在运行时导致异常(UnsupportedOperationException)。换句话说:final意味着你不能让yourSetField指向另一个对象(并且编译器保证它)。 unmodifiableSet表示您无法在集合中添加或删除对象。它将编译但会在运行时中断。

答案 1 :(得分:4)

听起来你想要静态,不是因为如何解决变化,而是因为它并不特定于你班级的任何一个实例。

我建议您有一个最终的静态变量,并在您的类型的静态初始化程序块中构建一个常规集,然后从常规集创建一个不可变集(例如the one中提供的Guava)一。将该不可变集引用分配给静态最终变量。完成工作。

答案 2 :(得分:4)

在课堂上你想要的东西是:

private static final Set<Foo> mySet;
static {
 // ...initialize contents here.  guava example looks like:
mySet = ImmutableSet.of( adc, 123, etc );

}

我会像Jon建议的那样使用Guava ImmutableSet,所以你将使用of( ... )方法或构建器界面(如果你有某种数据源 - 如果你是硬编码的话)数据,只需使用()),这两者都在API中得到了很好的体现。其他选项包括通过Collections中的不可修改方法进行换行。

答案 3 :(得分:2)

我想你知道static的含义。正如您所提到的,Set而不是内容,是一个“常量”,也就是说,没有实例可以在其中放置不同的Set,您最好将其设为final

所有实例的最终静态集都是相同的,并且所有实例都可以更改该集的内容

现在出现了另一个问题;并发性。如果您的班级m的多个实例同时对该集合进行了修改,该怎么办? Set应该做什么?您可以通过将您的集合包装在Synchronized Set中来捕获它。

总而言之,我认为声明应如下:

private static final Set<YourElement> mySet = Collections.synchronizedSet(new HashSet());

其中您预先知道的内容可以在Bozho显示的静态块中填充,其他元素可以在运行时添加。

这样的宣言就像

这样的陈述
void myFoo() {
  mySet = new HashSet(); // will fail as it's final
}

将失败,因为它应该这样做,并且该集合的并发更新将起作用。

如果你需要一个具有常数值的Set,你可以这样做:

private static final Set<YourElement> mySet;
static {
   Set<YourElement> tmpSet = new HashSet();
   tmpSet.add(...);
   mySet = Collections.unmodifiableSet(tmpSet);
}

但我发现其他人是第一个:)