3

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

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

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

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

添加:

好的。感谢答案,我明白它应该是“最终的”和“静态的”(因为它是一个常量集并且它不会与任何特定实例相关联,它应该对类的所有实例都是可见的)。但是我仍然有问题。我想使用“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. 里面有
什么?这个方法有什么作用? 3.做什么?在后面的例子中,我们给这个方法一个空集。做什么的? 4. 我们为什么要声明为 final? 5.具体是做什么的?我认为根据名称它会生成一个无法修改的集合。但是为什么声明为不够呢?比它也将是恒定的。SomeType()fillSet
fillSet
tempSet
unmodifiableSetyourSetFieldfinal

4

4 回答 4

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

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

首先 - 神秘的 <> 括号: 我假设您使用的是 Java 1.5 或更高版本并使用了generics。简而言之——如果你声明一个 List 类型的变量,它就包含 Objects。如果您希望将字符串保留在其中,则必须在从列表中检索它们时强制转换它们。例子

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。我写了关于 Lists 的文章,因为从 Lists 中检索更方便。我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. 使用的参数setfillSet添加了一个元素:set.add(new SomeType());
  2. 因为我不知道你想在你的集合中保留什么,所以我命名了它的元素类型SomeType。它将被替换为您要使用的类型。new SomeType();创建一个(假设的)SomeType调用其无参数构造函数的实例。
  3. fillSet完全符合它的名字的意思——接受一个集合并填充它(向它添加一些值)。我们给它一个空集合,因此我们得到一个包含元素的集合fillSetfillSet是一个放置所有初始化集合的代码的地方。把这个分开就好了。
  4. tempSet是静态初始化块中的局部变量,它被分配一次并且永远不会重新分配。为了表达这一点,我宣布它是最终的。这是我使用findBugs养成的习惯,我认为它对可读性有好处。
  5. 将 yourSetField 设为 final 意味着yourSetField = new HashSet<SomeType>()一旦初始化就无法编写。但是任何可以访问的人都yourSetField可以编写yourSetField.add(...). 如果yourSetField是 unmodifiableSet,添加到它会导致运行时异常 ( UnsupportedOperationException)。换句话说: final 意味着你不能yourSetField指向另一个对象(编译器保证它)。unmodifiableSet 意味着您不能在集合中添加或删除对象。它会编译但会在运行时中断。
于 2010-03-10T15:29:03.463 回答
5

在你的课堂上,你想要这样的东西:

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

}

我会ImmutableSet按照 Jon 的建议使用 Guava,因此您将使用该of( ... )方法或构建器界面(如果您有某种数据馈送 - 如果您对数据进行硬编码,只需使用 of()),这两者API中很好地涵盖了。其他选项包括通过 Collections 中的不可修改方法进行包装。

于 2010-03-10T15:47:54.313 回答
4

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

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

于 2010-03-10T15:05:02.600 回答
2

我想你知道什么static意思。正如您提到的那样,集合,而不是内容,是一个“常量”,也就是说,没有实例可以在那里放置不同的集合,您最好制作它final

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

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

但我看到别人是第一个:)

于 2010-03-10T15:18:37.283 回答