枚举元素之前的静态初始化

Static Initialization Before Enum Elements

在 Java 中枚举元素初始化之前不可能进行静态初始化,因为枚举元素总是必须先声明和初始化。

如果静态初始化不依赖于枚举元素,那么它在元素之后的执行顺序不是问题。否则,如果初始化依赖于枚举元素,则会造成麻烦。但是,Java 枚举提供了能够满足这种依赖性的 values() 方法。但是调用 values() 可能是一个开销,因为 values() 方法每次都会复制内部静态数组。

例如(编辑于postWhy can't enum's constructor access static fields?):

public enum Day {
    SUNDAY("Sun"), MONDAY("Mon"), TUESDAY("Tue"), WEDNESDAY("Wed"), THURSDAY("Thu"), FRIDAY("Fri"), SATURDAY("Sat");

    private final String abbreviation;

    private static final Map<String, Day> ABBREV_MAP = new HashMap<String, Day>();

    static
    {
        for(Day day:values())
            ABBREV_MAP.put(day.abbreviation, day);
    }
    private Day(String abbreviation) {
        this.abbreviation = abbreviation;
        //ABBREV_MAP.put(abbreviation, this); this is not valid
    }

    public String getAbbreviation() {
        return abbreviation;
    }

    public static Day getByAbbreviation(String abbreviation) {
        return ABBREV_MAP.get(abbreviation);
    }
}

如示例中所示,如果我能够在枚举元素之前初始化 HashMap,我将不需要静态初始化代码块,因此调用生成副本的 values() 方法。构造函数中注释掉的代码就足够了。

我的问题是,由于首先初始化枚举元素的义务可能会导致不必要的开销,是否可以消除这种开销,或者换句话说,是否可以在枚举元素之前进行静态初始化?如果不是,是否只是为了代码的可读性?我假设首先声明枚举元素与某些答案中提到的安全初始化无关。

在这个问题上,我说了一个开销,即使大多数时候它很小,但至少值得深入理解和集思广益。

作为心理瑜伽(想想编译器必须做什么):

public enum Day {

    SUNDAY(A.Sun), MONDAY(A.Mon), TUESDAY(A.Tue),
    WEDNESDAY(A.Wed), THURSDAY(A.Thu), FRIDAY(A.Fri), SATURDAY(A.Sat);

    private enum A {
        Sun(Day.SUNDAY), Mon(Day.MONDAY), Tue(Day.TUESDAY), Wed(Day.WEDNESDAY),
        Thu(Day.THURSDAY), Fri(Day.FRIDAY), Sat(Day.SATURDAY);

        private final Day day;

        A(Day day) {
            this.day = day;
        }
    }

    private final A abbrev;

    Day(A abbrev) {
        this.abbrev = abbrev;
    }

    public static Day getByAbbreviation(String abbreviation) {
        return A.valueOf(abbreviation).day;
    }
}

太变态了

Joop Eggen 上面的回答启发了我使用 static inner classes,尽管这是一个明显的解决方法和技巧,但它向我展示了消除这种开销并非不可能,而且它可以在枚举元素之前进行静态初始化。这是一次很好的头脑风暴。

对于值 table 和构造函数,我的最终代码将如下所示,将删除静态块并消除开销:

//HashMap in inner class
public static class InnerMap
{
    private static final Map<String, Day> ABBREV_MAP = new HashMap<String, Day>();
}
//Constructor
private Day(String abbreviation) {
    this.abbreviation = abbreviation;
    InnerMap.ABBREV_MAP.put(abbreviation, this);
}

这是有效的,因为静态初始化发生在第一次使用 class 时,在这种情况下是内部 class。