来自资源文件的final变量

643ylb08  于 2021-07-13  发布在  Java
关注(0)|答案(5)|浏览(338)

我有一个带有一些最终变量的活动。我将它们的值(假设它们都是字符串)提取到一个资源文件中。
问题是:
如果我在示例化时直接分配它们(如下所示): 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' 问题是:如何从资源文件中获得要分配的最终变量?如果这是不可能的,什么是解决方案的最佳实践?
提前谢谢。

34gzjxbg

34gzjxbg1#

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

yqyhoc1h

yqyhoc1h2#

答案很简单,你不能。
声明的变量 final 只能在示例化对象时设置(即在构造函数中或使用初始化器代码)。
任意使用 getResources().getString(R.string.preference_name); 所有时间或使用非最终变量。
复杂的答案是你可以,但你不应该。
当你声明一个变量 final 编译器和虚拟机使用它来进行优化和假设。它可以做到这一点,因为变量是保证永远不变的。在初始化后更改它会导致非常奇怪的错误,所以绝对不应该这样做。
以下是您的操作方法:

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;
    }
}

这将输出:
你好,世界!
你好,火星!
太好了,所以我们改变了最后一个变量。没问题吧?
好吧,换个例子:

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;
    }
}

这将输出:
你好,世界!
你好,世界!
等等,什么?
问题是编译器可以看到 message 永远都是 "Hello World!" 所以它是内联的 "Hello World!" 而不是我们的电话 f.getMessage() . 如果在调试器中运行此命令,您将看到调试器将示例中的更新消息反映到 "Hello Mars!" 但由于变量实际上从未被访问过,因此它不会影响程序的结果。
总结一下:您可以通过反射来更新final字段(假定没有安全管理器阻止您这样做),但是您不应该这样做,因为它会产生非常奇怪的副作用。
我不负责,如果你的房子得到白蚁或你的猫着火,如果你真的决定执行这个。

sh7euo9m

sh7euo9m3#

使用应用程序上下文:
创建应用程序类:

public class MyApplication extends Application {

    private static Context mContext;

    @Override
    public void onCreate() {
        super.onCreate();
        mContext = getApplicationContext();
    }

    public static String getStr(int resId) {
        return mContext.getString(resId);
    }

}

在您的清单中使用它:

<application
    android:name=".MyApplication"
    ...

在应用程序中的任意位置调用它:

static final String NAME = MyApplication.getStr(R.string.app_name);
pxyaymoc

pxyaymoc4#

您可以使用工厂模式来解决这个问题。
生成器是一个类,它聚合了创建对象所需的数据,完成后,您只需构建类。
在这种方法中,工厂也可以使用生成对象所需的数据,他可以轻松地创建对象,并初始化 final 调用其构造函数时的字段。
你会有

class MyFactory {
   private Resource getResources() { ... }
   public MyObject build() { 
       String perference_name = getResources().getString(R.string.preference_name);
       /...
       return new MyObject(perfence_name ,....);
   }
}
smdncfj3

smdncfj35#

您只需将其声明为final方法:

private final String PREFERENCE_NAME() {
    return getResources().getString(R.string.preference_name);}

相关问题