我应该如何声明一个对类的每个实例都可见的常量集?

发布于 2024-08-24 05:14:11 字数 1788 浏览 2 评论 0原文

我想在我的类中设置一个常量,该常量对该类的所有实例都可见。

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

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

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

添加:

好的。感谢答案,我明白它应该是“最终”和“静态”(因为它是一个常量集,并且不会与任何特定实例关联,它应该对该类的所有实例可见)。但是我仍然有一个问题。我想使用“add”指定集合,如果它是常量(最终),我无法添加到集合中。此外,我无法更改方法外部变量的值(为什么?)。无论如何,我不坚持使用“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。 fillSet 中的 SomeType() 是什么?这个方法有什么作用?
3。 fillSet 的作用是什么?稍后在示例中,我们为此方法提供了一个空集。做什么的?
4。我们将 tempSet 声明为 Final 有何用处?
5。 unmodifyingSet 到底做什么?我认为根据名称它会生成一个无法修改的集合。但为什么将 yourSetField 声明为 final 还不够呢?比它也将是恒定的。

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:

Can anybody explain me, pleas, the code given by 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 fillSet method 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 fillSet do? Later in the example we give an empty set to this method. What for?

4. What for do we declare tempSet as final?

5. What exactly unmodifiableSet do? According to the name it generate a set which cannot be modified, I think. But why would it be insufficient to declare yourSetField as final? Than it would be constant too.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

满天都是小星星 2024-08-31 05:14:11

您是否希望将元素添加到集合中一次,然后只读取其内容,还是希望能够随时向集合中添加元素?如果您创建它一次,请按如下方式操作:

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 的答案。

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

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

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

需要强制转换为 String。此外,没有什么可以阻止您将 Bi​​gDecimal 添加到 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<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. 使用了fillSet的参数set。其中添加了一个元素: set.add(new SomeType());
  2. 由于我不知道要在集合中保留什么,因此我将其元素的类型命名为 某种类型。将其替换为您要使用的类型。 new SomeType(); 创建一个(假设的)SomeType 调用其无参数构造函数的实例。
  3. fillSet 的作用正是其名称的含义 - 获取一个集合并填充它(向其中添加一些值)。我们给它一个空集,这样我们就得到一个包含 fillSet 元素的集合。 fillSet 是您应该放置初始化集合的所有代码的地方。把这个分开就好了。
  4. tempSet 是静态初始化块中的局部变量,它被分配一次并且永远不会重新分配。为了表达这一点,我宣布它是最终的。这是我使用 findBugs 养成的习惯,我认为它有利于可读性。
  5. 将 yourSetField 设置为最终值意味着一旦初始化,就无法编写 yourSetField = new HashSet() 。但是任何可以访问 yourSetField 的人都可以编写 yourSetField.add(...)。如果 yourSetField 是不可修改的Set,则向其添加将导致运行时异常 (UnsupportedOperationException)。换句话说:final 意味着你不能让 yourSetField 指向另一个对象(编译器保证这一点)。 unmodifierSet 意味着您不能从集合中添加或删除对象。它将编译但会在运行时中断。

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.

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

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.

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:

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 SomeType in 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.

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

  1. The parameter set of fillSet is used. There is an element added to it: set.add(new SomeType());
  2. 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) SomeType calling its parameterless constructor.
  3. fillSet does 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 elements fillSet has put in it. fillSet is a place where you should put all the code that initializes your set. It's good to have this separated.
  4. tempSet is 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 findBugs and I think it's good for readability.
  5. Making yourSetField final means you can't write yourSetField = new HashSet<SomeType>() once it is initialized. But anyone who can access yourSetField can write yourSetField.add(...). If yourSetField is an unmodifiableSet, adding to it will cause an exception in runtime (UnsupportedOperationException). In other words: final means you cannot make yourSetField point 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.
戒ㄋ 2024-08-31 05:14:11

在你的课堂上,你想要类似的东西:

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

}

我会像乔恩建议的那样使用 Guava ImmutableSet ,所以你会使用 of( ... ) 方法或构建器接口(如果您有某种数据源 - 如果您对数据进行硬编码,只需使用 of()),API 中很好地涵盖了这两个方面。其他选项包括通过集合中不可修改的方法进行包装。

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 ImmutableSet as 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.

吻风 2024-08-31 05:14:11

听起来您确实想要静态,不是因为如何处理更改,而是因为它不特定于您的类的任何一个实例。

我建议你有一个最终的静态变量,并在你的类型的静态初始化块中构建一个常规集,然后创建一个不可变集(例如 番石榴)来自常规的。将该不可变集引用分配给静态最终变量。工作完成了。

It sounds like you do want 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 one provided in Guava) from the regular one. Assign that immutable set reference to your static final variable. Job done.

各空 2024-08-31 05:14:11

我想你知道static是什么意思。正如您提到的,集合(而不是内容)是“常量”,也就是说,没有实例可以在那里放置不同的集合,您最好将其设为最终

最终的静态 Set 对于所有实例都是相同的,并且该 Set 的内容可以由所有实例更改。

现在出现了一个不同的问题;并发。如果您的类的多个实例同时修改 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);
}

但我看到其他人是第一个:)

I think you know what static means. 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.

A final static Set is the same for all instances, and the contents of that Set can be changed by all instances.

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.

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.

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:

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 :)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文