java - 资源文件中的最终变量

标签 java android android-activity android-studio

我有一个包含一些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/

相关文章:

android - 澄清 OneOffTask 上的 setUpdateCurrent

android - 想在调用时实现选项

Android 服务将一个 Activity 的多个实例放入历史记录中

android - 如何获得 PendingIntent.getActivity 的结果

java - 在maven appsembler程序脚本中设置java cmd

java - 如何在代理后面使用 Apache Olingo

android - 如何完全隐藏用于将输入类型更改为数字 SIP 的 EditText

java - 如何动态地将对象添加到 Hibernate 查询中

java - 使用selenium自定义Chrome webdriver保存文件下载时不提示保存或丢弃文件

Android:使用相机源代码时遇到问题