我有一个包含一些final 变量的 Activity 。我将它们的值(假设它们都是字符串)提取到一个资源文件中。
问题:
如果我在实例化时直接赋值(如下):
private final String PREFERENCE_NAME = getResources().getString(R.string.preference_name);
我收到以下错误:
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.Resources android.content.Context.getResources()' on a null object reference
我明白这个问题; onCreate() 方法尚未被调用,这意味着我无法访问与上下文相关的方法 (getResources()
)。
如果我想在 Activity 的 onCreate() 方法中赋值,我会得到错误 Cannot assign value to final variable 'PREFERENCE_NAME'
问题是:如何从资源文件中获取最终变量?如果这不可能,解决方案的最佳实践是什么?
提前致谢。
最佳答案
简单的答案是你不能。
声明为 final
的变量只能在实例化对象时设置(即在构造函数中或使用初始化代码)。
一直使用 getResources().getString(R.string.preference_name);
或使用非最终变量。
复杂的答案是您可以但不应该。
当您声明变量 final
时,编译器和 VM 使用它来进行优化和假设。它可以这样做是因为变量保证永远不会改变。在初始化后更改它可能会导致非常奇怪的错误,因此您绝对不应该这样做。
这是你如何做的:
public class FinalMessage {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
FinalMessage f = new FinalMessage("Hello World!");
System.out.println(f.getMessage());
f.changeFinalMessage("Hello Mars!");
System.out.println(f.getMessage());
}
private final String message;
public FinalMessage(String message) {
this.message = message;
}
void changeFinalMessage(String newMessage) throws IllegalAccessException, NoSuchFieldException {
final Field field = FinalMessage.class.getDeclaredField("message");
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(this, newMessage);
}
String getMessage() {
return message;
}
}
这将输出:
Hello World!
Hello Mars!
太好了,所以我们更改了一个 final 变量。没问题吧?
好吧,改用这个例子:
public class FinalMessage {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
FinalMessage f = new FinalMessage();
System.out.println(f.getMessage());
f.changeFinalMessage("Hello Mars!");
System.out.println(f.getMessage());
}
private final String message = "Hello World!";
void changeFinalMessage(String newMessage) throws IllegalAccessException, NoSuchFieldException {
final Field field = FinalMessage.class.getDeclaredField("message");
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(this, newMessage);
}
String getMessage() {
return message;
}
}
这将输出:
Hello World!
Hello World!
等等,什么?
问题是编译器可以看到变量 message
总是 "Hello World!"
所以它内联 "Hello World!"
而不是我们对 f.getMessage()
的调用。如果您在调试器中运行它,您将看到调试器将实例中的更新消息反射(reflect)到 "Hello Mars!"
但由于该变量实际上从未被访问过,因此不会影响程序的结果.
总结一下:您可以通过反射更新 final 字段(假设没有 Security Manager 阻止您这样做),但您不应该这样做,因为它会产生非常非常奇怪的副作用。
如果您真的决定实现此功能,如果您的房子有白蚁或您的猫着火,我概不负责。
关于java - 资源文件中的最终变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29913722/