资源文件中的最终变量

Shi*_*dem 4 java android android-activity android-studio

我有一个带有一些最终变量的活动。我将它们的值(假设它们都是字符串)提取到资源文件中。

问题:

如果我直接将它们分配给实例化(如下):

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())。

如果我想在活动的 onCreate() 方法中分配值,我会收到错误 Cannot assign value to final variable 'PREFERENCE_NAME'

问题是:如何从资源文件中分配我的最终变量?如果这是不可能的,那么解决方案的最佳实践是什么?

提前致谢。

Ran*_*niz 5

简单的答案是你不能。

声明的变量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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这将输出:

你好,世界!
火星你好!

太好了,所以我们更改了最终变量。没问题吧?

好吧,以这个例子代替:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这将输出:

你好,世界!
你好,世界!

等等,什么?

问题是编译器可以看到变量message总是会被内"Hello World!"联,"Hello World!"而不是我们对f.getMessage(). 如果您在调试器中运行它,您将看到调试器将实例中的更新消息反映到,"Hello Mars!"但由于该变量实际上从未被访问过,因此不会影响程序的结果。

总结一下:您可以通过反射更新最终字段(假设没有安全管理器阻止您这样做),但您不应该这样做,因为它可能会产生非常非常奇怪的副作用。

如果您真的决定实施这一点,如果您的房子有白蚁或您的猫着火,我概不负责。

  • 哇,读得很好。感谢您提供了很好解释的示例以及为什么不这样做。+1。 (2认同)

Dre*_*gen 4

在这种情况下,我认为最好的办法就是多次调用资源。您仍然只需更改一处的值,并且对 的调用getResources()并不昂贵。