简体   繁体   English

从实例初始化程序更新静态final字段

[英]Updating a static final field from an instance initializer

Java prohibits access of a final static field from an initializer. Java禁止从初始化程序访问最终的静态字段。 For example: 例如:

public enum Example {
    ValueA("valueAA", "valueAB"),
    ValueB("valueBA", "valueBB");

    final static Map<String, Example> exampleByPropertyA = new HashMap<>();

    final String propertyA;
    final String propertyB;

    Example(String propertyA, String propertyB) {
        this.propertyA = propertyA;
        this.propertyB = propertyB;

        Example.exampleByPropertyA.put(propertyA, this); // <- Not permitted
    }
}

However, if the update to the static Map is performed in a separate method that is called by the initializer, this is fine. 但是,如果静态Map的更新是在初始化程序调用的单独方法中执行的,那么这很好。 For example: 例如:

public enum Example {
    ValueA("valueAA", "valueAB"),
    ValueB("valueBA", "valueBB");

    final static Map<String, Example> exampleByPropertyA = new HashMap<>();

    final String propertyA;
    final String propertyB;

    Example(String propertyA, String propertyB) {
        this.propertyA = propertyA;
        this.propertyB = propertyB;

        addExample(this);
    }

    private addExample(Example example) {
        Example.exampleByPropertyA.put(example.propertyA, example); // <- Permitted
    }
}

Given this context, my question is: Does a call to a member method constitute a "freeze action" or is it indicative to the JVM that the object is, for all intents and purposes, "initialized"? 鉴于此上下文,我的问题是:对成员方法的调用是否构成“冻结操作”,还是指向JVM,对于所有意图和目的,该对象是“初始化的”? Curious why this makes a difference. 好奇为什么这会产生影响。

I've done some searching, but haven't found anything that articulates this well. 我已经做了一些搜索,但没有找到任何能够清楚地表达出来的东西。

Thank you in advance! 先感谢您!

Does a call to a member method constitute a "freeze action" or is it indicative to the JVM that the object is, for all intents and purposes, "initialized"? 对成员方法的调用是否构成“冻结操作”,或者它是否指示JVM对于所有意图和目的,“初始化”对象是什么? Curious why this makes a difference. 好奇为什么这会产生影响。

The problem is that your class is initialised top to bottom. 问题是你的课程是从上到下初始化的。 This means your static fields have not been initialised yet ie your Map is null . 这意味着您的静态字段尚未初始化,即您的Map为null

Another approach is to add a static initialisation block to be called after everything has been initialised. 另一种方法是添加一个静态初始化块,以便在所有内容初始化后调用。

static {
    for (Example e: values()) {
        addExample(e);
    }
}

private static addExample(Example example) {
    Example prev = exampleByPropertyA.put(example.propertyA, example);
    assert prev == null;
}

NOTE: You can see a final variable before it is initialised. 注意:您可以在初始化之前查看最终变量。 This means final can have a before and after value even without using reflection. 这意味着即使不使用反射, final也可以具有前后值。

public class A {
    final String text = getText();

    private String getText() {
        System.out.println("text= " + text);
        return "is set";
    }

    public static void main(String... args) {
        new A().getText();
    }
}

prints 版画

text= null
text= is set

Using reflection you can alter final fields even after initialisation though you should avoid doing this unless there is no other option. 使用反射即使在初始化之后也可以改变final字段,但除非没有其他选项,否则应该避免这样做。

The correct way to do what you're trying to do, is to write a static initializer, which runs after all the enums have been created. 执行您要执行的操作的正确方法是编写静态初始化程序,该程序创建所有枚举之后运行。

Defensive programming: You should also add a simple check to guard against programming errors. 防御性编程:您还应该添加一个简单的检查以防止编程错误。

public enum Example {
    ValueA("valueAA", "valueAB"),
    ValueB("valueBA", "valueBB");

    final static Map<String, Example> exampleByPropertyA = new HashMap<>();
    static {
        for (Example ex : values())
            if (exampleByPropertyA.put(ex.propertyA, ex) != null)
                throw new IllegalStateException("Duplicate propertyA: " + ex.propertyA);
    }

    final String propertyA;
    final String propertyB;

    Example(String propertyA, String propertyB) {
        this.propertyA = propertyA;
        this.propertyB = propertyB;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM