java 我应该如何声明一个对类的每个实例可见的常量集?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2417886/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How should I declare a constant set visible for every instance of the class?
提问by Roman
I would like to have a constant set in my class which would be visible for all instances of the class.
我想在我的班级中设置一个常数,该常数对班级的所有实例都是可见的。
First, I do not know if I need to declare it as "static". As far as I understand any changes of a static field (done by one of the instances) will be seen by other instances (so static variable is not bound to a specific instance). Moreover, a static field can be changes without usage of any instance (we work directly with the class). So, all these special properties of the static field are related with the way how it can be changed and what be the effect of these changes. But in my case I would like to have a constant (so the "changes" issues are not relevant here). So, probably I do not need to use "static". Right?
首先,我不知道是否需要将其声明为“静态”。据我所知,静态字段的任何更改(由其中一个实例完成)都会被其他实例看到(因此静态变量未绑定到特定实例)。此外,静态字段可以在不使用任何实例的情况下进行更改(我们直接使用该类)。因此,静态字段的所有这些特殊属性都与它可以如何更改以及这些更改的效果有关。但在我的情况下,我想要一个常量(所以“更改”问题在这里不相关)。所以,可能我不需要使用“静态”。对?
Second, my set will contain a lot of elements and I do not want to define the value of the set at once (when I create the variable). In other words I would like to declare a set, then add elements to this set step by step. But I cannot do it if I work with constants. Is it possible to specified value of the set and then make it constant?
其次,我的集合将包含很多元素,我不想一次定义集合的值(当我创建变量时)。换句话说,我想声明一个集合,然后一步一步地向这个集合添加元素。但是如果我使用常量,我就做不到。是否可以指定集合的值然后使其恒定?
Third, I have realized that there can be some problems if I try to change value of variables outside of any method. So, how does it work?
第三,我意识到如果我尝试在任何方法之外更改变量的值,可能会出现一些问题。那么它是怎样工作的?
ADDED:
添加:
OK. Thanks to the answer I understood that it should be "final" and "static" (since it is a constant set and it will not be associated with any particular instance, it just should be visible to all instances of the class). However I still have a problem. I wanted to specify the set using "add" and I cannot add to the set if it is constant (final). Moreover, I cannot change values of the variables outside methods (why?). Anyway, I do not insist on the usage of "add" to define the set. I am ready to define it at once. But I do not know how to do it. I tried things like that:
行。由于答案我明白它应该是“最终的”和“静态的”(因为它是一个常量集并且它不会与任何特定实例相关联,它应该对类的所有实例可见)。但是我仍然有问题。我想使用“添加”指定集合,如果它是常量(最终),我就不能添加到集合中。此外,我无法在方法之外更改变量的值(为什么?)。无论如何,我不坚持使用“添加”来定义集合。我准备好立即定义它。但我不知道该怎么做。我试过这样的事情:
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"}));
And they did not work.
他们没有工作。
ADDED 2:
添加 2:
Can anybody explain me, pleas, the code given by Tadeusz Kopec?
任何人都可以向我解释一下,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. The fillSetmethod has one variable called "set". Why it is not used in the method?
2. What is SomeType()in the fillSet? What does this method do?
3. What does fillSetdo? Later in the example we give an empty set to this method. What for?
4. What for do we declare tempSetas final?
5. What exactly unmodifiableSetdo? According to the name it generate a set which cannot be modified, I think. But why would it be insufficient to declare yourSetFieldas final? Than it would be constant too.
1. 该fillSet方法有一个名为“set”的变量。为什么在方法中不使用它?
2.什么是SomeType()在fillSet?这个方法有什么作用?
3.有什么作用fillSet?在后面的例子中,我们给这个方法一个空集。做什么的?
4. 为什么我们声明tempSet为final?
5.具体unmodifiableSet做什么?根据名称它生成一个不能修改的集合,我想。但是为什么声明yourSetField为还不够final呢?比它也将是恒定的。
回答by Tadeusz Kopec
Do you want to add elements to your set once and then only read its contents or do you want to be able to add elements to it at any time? If you create it once, do it like this:
您是想将元素添加到您的集合中,然后只读取其内容,还是希望能够随时向其添加元素?如果您创建一次,请执行以下操作:
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);
}
}
Now - it's private, so no one outside your class can access it. And its unmodifiable, so no one can change its content.
现在 - 它是私有的,因此您班级以外的任何人都无法访问它。而且它是不可修改的,所以没有人可以改变它的内容。
If you want to add elements at any time, you have a concurrency problem - read extraneon's answer.
如果您想随时添加元素,就会遇到并发问题——阅读 extraneon 的回答。
EDIT
As requested I explain what this code does.
编辑
根据要求,我解释了这段代码的作用。
First - mysterious <> brackets:I assumed you are using Java 1.5 or higher and used generics. In few words - if you declare a variable of type List, it holds Objects. If you wish to keep Strings in it, you have to cast them when you retrieve them from your List. Example
首先 - 神秘的 <> 括号:我假设您使用的是 Java 1.5 或更高版本并使用了generics。简而言之——如果你声明一个 List 类型的变量,它就包含对象。如果您希望在其中保留字符串,则必须在从列表中检索它们时强制转换它们。例子
List myList = new ArrayList();
myList.add("Hello, my Jon Skeet Number decreases");
String firstElement = (String) myList.get(0);
The cast to String is required. Moreover nothing prevents you from adding BigDecimal to myList. But if you retrieve it and try to cast to String, you get ClassCastException.
强制转换为 String 是必需的。此外,没有什么可以阻止您将 BigDecimal 添加到 myList。但是,如果您检索它并尝试转换为 String,则会得到 ClassCastException。
myList.add(0, BigDecimal.ZERO); // perfectly legal
String anotherString = (String) myList.get(0); // compiles, but ClassCastException at runtime
So Java 1.5 introduces generics. You can specify, that List can contain only Strings. Syntax uses <> brackets:
所以 Java 1.5 引入了泛型。您可以指定,该列表只能包含字符串。语法使用 <> 括号:
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
Same applies to other collections, like Sets. I wrote about Lists because retrieving from Lists is more convenient. I used SomeTypein my example because I didn't know what you want to keep in your Set. You should replace it with type of objects you wish to store.
这同样适用于其他集合,如 Sets。我写了关于 Lists 是因为从 Lists 中检索更方便。我SomeType在我的例子中使用,因为我不知道你想在你的 Set 中保留什么。您应该将其替换为您希望存储的对象类型。
Now - static blocks.There are two ways to initialize static fields - directly in their declaration:
现在 - 静态块。有两种方法可以初始化静态字段 - 直接在它们的声明中:
static private int instanceCount = 0;
This is useful if initial value is a simple expression.
Or in static initialization block
如果初始值是一个简单的表达式,这很有用。
或者在静态初始化块中
static {
// some code, that can use class static variables, class static methods, declare its own variables etc.
}
This is useful, if initial value for some static fields needs some more complicated computations.
如果某些静态字段的初始值需要一些更复杂的计算,这很有用。
And now your questions
现在你的问题
- The parameter
setoffillSetis used. There is an element added to it:set.add(new SomeType()); - As I didn't know what you want to keep in your set, I named the type of its elements
SomeType. It is to be replaced with the type you want to use.new SomeType();creates an instance of (hypothetical)SomeTypecalling its parameterless constructor. fillSetdoes exactly what its name means - takes a set and fills it (adds some values to it). We give an empty set to it so that as a result we get a set with elementsfillSethas put in it.fillSetis a place where you should put all the code that initializes your set. It's good to have this separated.tempSetis a local variable in static initialization block which is assigned once and never reassigned. To express this I declare it final. It's a habit I got from using findBugsand I think it's good for readability.- Making yourSetField final means you can't write
yourSetField = new HashSet<SomeType>()once it is initialized. But anyone who can accessyourSetFieldcan writeyourSetField.add(...). IfyourSetFieldis an unmodifiableSet, adding to it will cause an exception in runtime (UnsupportedOperationException). In other words: final means you cannot makeyourSetFieldpoint to another object (and compiler guarantees it). unmodifiableSet means you cannot add or remove objects from the set. It will compile but will break in runtime.
- 参数
set的fillSet被使用。添加了一个元素:set.add(new SomeType()); - 由于我不知道你想在你的集合中保留什么,我命名了它的元素的类型
SomeType。将替换为您要使用的类型。new SomeType();创建一个(假设的)SomeType调用其无参数构造函数的实例。 fillSet完全符合其名称的含义 - 获取一个集合并填充它(向其添加一些值)。我们给它一个空集,结果我们得到一个包含元素的集合fillSet。fillSet是您应该放置初始化您的集合的所有代码的地方。把这个分开就好了。tempSet是静态初始化块中的一个局部变量,它被赋值一次,永远不会被重新赋值。为了表达这一点,我宣布它是最终的。这是我使用findBugs养成的习惯,我认为它有利于提高可读性。- 使 yourSetField 成为 final 意味着
yourSetField = new HashSet<SomeType>()一旦初始化就不能写入。但是任何可以访问的人都yourSetField可以编写yourSetField.add(...). 如果yourSetField是 unmodifiableSet,添加到它会导致运行时异常 (UnsupportedOperationException)。换句话说: final 意味着你不能yourSetField指向另一个对象(并且编译器保证它)。unmodifiableSet 意味着您不能从集合中添加或删除对象。它会编译但会在运行时中断。
回答by Carl
In your class you want something like:
在你的课堂上,你想要这样的东西:
private static final Set<Foo> mySet;
static {
// ...initialize contents here. guava example looks like:
mySet = ImmutableSet.of( adc, 123, etc );
}
I would go with the Guava ImmutableSetas Jon suggests, so you'd be using the of( ... )method or the builder interface (if you have some sort of feed of data - if you are hardcoding the data, just use of()), both of which are pretty well covered in the API. Other options include wrapping via the unmodifiable method from Collections.
我会ImmutableSet按照 Jon 的建议使用 Guava ,因此您将使用该of( ... )方法或构建器界面(如果您有某种数据提要 - 如果您对数据进行硬编码,只需使用 of()),两者API 中已经很好地涵盖了。其他选项包括通过集合中的不可修改方法进行包装。
回答by Jon Skeet
It sounds like you dowant static, not because of how changes are addressed but because it's not specific to any one instance of your class.
听起来您确实想要静态,不是因为如何解决更改,而是因为它不特定于您的类的任何一个实例。
I suggest you have a final static variable, and in a static initializer block for your type you build a regular set, then create an immutable set (e.g. the oneprovided in Guava) from the regular one. Assign that immutable set reference to your static final variable. Job done.
我建议你有一个最终的静态变量,和静态初始化块为你的类型,你建一个常规组,然后创建一个不可改变的集(例如一所提供番石榴从常规one)。将该不可变集引用分配给您的静态最终变量。任务完成。
回答by extraneon
I think you know what staticmeans. As you mentioned the Set, but not the contents, is a "constant", that is, no instance may put a different Set there, you would do well to make it final.
我想你知道什么static意思。正如你提到的 Set 而不是内容,是一个“常量”,也就是说,没有实例可以在那里放置不同的 Set ,你会做得很好 make it final。
A final static Set is the same for all instances, and the contentsof that Set can be changed by all instances.
最终的静态 Set 对于所有实例都是相同的,并且所有实例都可以更改该 Set的内容。
A different problem now arises; concurrency. What if multiple instances of your class m,odify the Set at the same time? What should the Set do? You could catch that by wrapping your set in a Synchronized Set.
现在出现了一个不同的问题;并发。如果你的类 m 的多个实例同时修改 Set 怎么办?Set应该怎么做?您可以通过将您的 set 包装在Synchronized Set 中来捕捉它。
All in all I think the declaration should look like:
总而言之,我认为声明应该如下所示:
private static final Set<YourElement> mySet = Collections.synchronizedSet(new HashSet());
where the contents which you know beforehand can be filled in the static block as Bozho showed, and other elements can be added run-time.
其中你事先知道的内容可以像Bozho所示的那样填充到静态块中,其他元素可以在运行时添加。
With such a declaration a statement like
有了这样的声明,就像这样的声明
void myFoo() {
mySet = new HashSet(); // will fail as it's final
}
will fail as it's supposed to do, and concurrent updates to the set will work.
将失败,并且对集合的并发更新将起作用。
If you need a Set with constant values, you could do:
如果您需要具有常量值的 Set,您可以执行以下操作:
private static final Set<YourElement> mySet;
static {
Set<YourElement> tmpSet = new HashSet();
tmpSet.add(...);
mySet = Collections.unmodifiableSet(tmpSet);
}
But I see someone else was first :)
但我看到别人是第一个:)

