枚举元素之前的静态初始化
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。
在 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。