Why can't enum's constructor access static fields? - java

Why can't enum's constructor access static fields and methods? This is perfectly valid with a class, but is not allowed with an enum.
What I'm trying to do is store my enum instances in a static Map. Consider this example code which allows lookup by abbreivation:
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>();
private Day(String abbreviation) {
this.abbreviation = abbreviation;
ABBREV_MAP.put(abbreviation, this); // Not valid
}
public String getAbbreviation() {
return abbreviation;
}
public static Day getByAbbreviation(String abbreviation) {
return ABBREV_MAP.get(abbreviation);
}
}
This will not work as enum doesn't allow static references in its constructor. It however works just find if implemented as a class:
public static final Day SUNDAY = new Day("Sunday", "Sun");
private Day(String name, String abbreviation) {
this.name = name;
this.abbreviation = abbreviation;
ABBREV_MAP.put(abbreviation, this); // Valid
}

The constructor is called before the static fields have all been initialized, because the static fields (including those representing the enum values) are initialized in textual order, and the enum values always come before the other fields. Note that in your class example you haven't shown where ABBREV_MAP is initialized - if it's after SUNDAY, you'll get an exception when the class is initialized.
Yes, it's a bit of a pain and could probably have been designed better.
However, the usual answer in my experience is to have a static {} block at the end of all the static initializers, and do all static initialization there, using EnumSet.allOf to get at all the values.

Quote from JLS, section "Enum Body Declarations":
Without this rule, apparently reasonable code would fail at run time
due to the initialization circularity inherent in enum types. (A
circularity exists in any class with a "self-typed" static field.)
Here is an example of the sort of code that would fail:
enum Color {
RED, GREEN, BLUE;
static final Map<String,Color> colorMap = new HashMap<String,Color>();
Color() {
colorMap.put(toString(), this);
}
}
Static initialization of this enum type would throw a NullPointerException because the static variable colorMap is
uninitialized when the constructors for the enum constants run. The
restriction above ensures that such code won’t compile.
Note that the example can easily be refactored to work properly:
enum Color {
RED, GREEN, BLUE;
static final Map<String,Color> colorMap = new HashMap<String,Color>();
static {
for (Color c : Color.values())
colorMap.put(c.toString(), c);
}
}
The refactored version is clearly correct, as static initialization occurs top to bottom.

maybe this is what you want
public enum Day {
Sunday("Sun"),
Monday("Mon"),
Tuesday("Tue"),
Wednesday("Wed"),
Thursday("Thu"),
Friday("Fri"),
Saturday("Sat");
private static final Map<String, Day> ELEMENTS;
static {
Map<String, Day> elements = new HashMap<String, Day>();
for (Day value : values()) {
elements.put(value.element(), value);
}
ELEMENTS = Collections.unmodifiableMap(elements);
}
private final String abbr;
Day(String abbr) {
this.abbr = abbr;
}
public String element() {
return this.abbr;
}
public static Day elementOf(String abbr) {
return ELEMENTS.get(abbr);
}
}

The problem solved via a nested class. Pros: it's shorter and also better by CPU consumption. Cons: one more class in JVM memory.
enum Day {
private static final class Helper {
static Map<String,Day> ABBR_TO_ENUM = new HashMap<>();
}
Day(String abbr) {
this.abbr = abbr;
Helper.ABBR_TO_ENUM.put(abbr, this);
}
public static Day getByAbbreviation(String abbr) {
return Helper.ABBR_TO_ENUM.get(abbr);
}

When a class is loaded in the JVM then static fields are initialized in the order in which they appear in code. For e.g.
public class Test4 {
private static final Test4 test4 = new Test4();
private static int j = 6;
Test4() {
System.out.println(j);
}
private static void test() {
}
public static void main(String[] args) {
Test4.test();
}
}
The output will be 0. Note that test4 initialization takes place in static initialization process and during this time j is not yet initialized as it appears later. Now if we switch order of static initializers such that j comes before test4. The output will be 6.But in case of Enums we cannot alter order of static fields. The first thing in enum must be the constants which are actually static final instances of enum type.Thus for enums its always guaranteed that static fields wont be initialized before enum constants.Since we cannot give any sensible values to static fields for use in enum constructor, it would be meaningless to access them in enum constructor.

Related

Pass class constants as parameter in java

I want to pass a class constants to function as parameter code is
public class XConstants {
public static final String DATA= "DATA";
public static final String SET = "Node";
}
public static void main(String[] args) {
foo(XConstants.DATA);
}
public static void foo(XConstants d){
System.out.println(d);
}
Here in the main method i am passing the XConstants.DATA to foo function but it gives me compile error of type miss match which is obvious because XConstants.DATA is type of String.
Similarly if i use enum and pass enum value to function parameter it will works perfectly fine. code is
enum Color{RED,BLUE}
public static void main(String[] args) {
bar(Color.RED);
}
public static void bar(Color d){
System.out.println(d);
}
Here enum is value is simply passing as a parameter.
I want to know that how should i change my code of XConstants so that it will work same as enum mentioned in the code are working (I know both are different things).
please note that i do not want to change the method signature like
public static void main(String[] args) {
foo(XConstants.DATA);
}
public static void foo(String d){
System.out.println(d);
}
It will work fine in this case because in this case type mis match conflict resolves.
To be short i want to know how should i change my XContants code ,r which design pattern should i use to achieving this working fine as it is working in the case of enum.
Any help will be greatly appreciated
enum Color{RED,BLUE} is similar to
class Color{
public final static Color RED = new Color("RED");
public final static Color BLUE = new Color("BLUE");
private String name;
private Color(String name){
this.name = name;
}
public String toString(){
return name;
}
//...rest of code like `values()`, and `ordinal()` methods
}
So if method is expecting Color it is possible to pass Color.RED, because RED it is instance of type Color.
Now depending on your requirements you can try to adapt your XConstants to this pattern.
I'm not sure why would you would want to do this when you already know that enums fit your purpose perfectly. If you're just curious to know if it's possible to achieve this with classes, read on.
Enums in many ways behave like classes. I think you'll already know that they can have fields, constructors and methods as well. But, the most important thing that concerns what interests you at the moment is that an enum constant's type is that of the enum itself.
So, to achieve this enum like behaviour you just have to model your class that way.
public class XConstants {
private String name;
public XConstants(String name) {
this.name = name;
}
#Override
public String toString() {
return name;
}
public static final XConstants DATA = new XConstants("DATA");
public static final XConstants SET = new XConstants("Node");
public static void main(String[] args) {
foo(XConstants.DATA);
foo(XConstants.SET);
}
public static void foo(XConstants d) {
System.out.println(d);
}
}
Output:
DATA
Node

Can an enum have a constructors for each of its constants

Please look at this link. In his book Effective Java, Joshua Bloch says
Note that the Operation constants are put into the stringToEnum map from a
static block that runs after the constants have been created.
Trying to make each constant put itself into the map from its own
constructor would cause a compilation error
. This is a good thing, because it would cause a NullPointerException
if it were legal.
Enum constructors aren’t permitted to access the enum’s static fields,
except for compile-time constant fields.
This restriction is necessary
because these static fields have not yet been initialized when the constructors run.
I have two questions
Can Enums have separate constructors for each constant?
Why are compile time constant fields accessible in constructors but not static fields?
Thanks
As for first question: you cannot have separate constructors, but you can work-around this in the following manner:
public enum EnumTest {
ONE() {
void init() {
val = 2;
}
},
TWO() {
void init() {
val = 1;
}
};
protected int val;
abstract void init();
EnumTest() {
init();
}
}
This way you technically have separate initialization methods for different constants.
Another way is to use initializer sections:
public enum EnumTest {
ONE() {{
val = 2;
}},
TWO() {{
val = 1;
}};
protected int val;
}
As for your second question: constant fields are not accessible during enum construction, because enum constants are accessible for static fields. For example, this code compiles correctly:
public enum EnumTest {
ONE, TWO;
public static final String ONE_STRING = ONE.toString();
}
If accessing ONE_STRING from the constructor were allowed, you would either had an endless initialization loop or accessing of not-yet-initialized enum constant.
No, and I don't think that is what Bloch means, although it has not been formulated in the best possible way. An enum can have constructors, as the enum Operation in the book has. What Bloch means with "its own constructor" is: when the constructor of Operation runs for that particular constant.
This is already answered by what you quoted above:
This restriction is necessary because these static fields have not yet been initialized when the constructors run.
The regular rules for constructors apply. You can have as many constructors as you want so long as they have different signatures. Different enum values can then be built using different constructors:
enum StringAndNumber {
Zero("zero"),
Five(5),
Pi("Pi", 3.14159);
private final String str;
private final Number num;
private StringAndNumber(String str) {
this.str = str;
this.num = null;
}
private StringAndNumber(Number num) {
this.num = num;
this.str = null;
}
private StringAndNumber(String str, Number num) {
this.str = str;
this.num = num;
}
}

If enums are implicitly static, why are they able to make use of the "this" keyword internally?

Please forgive the beginner-level question, but I'm confused by the implicit static status of enums.
On one hand, you can't declare them within methods because they are implicitly static, and you can reference them from a static context like any other static class.. but on the other, internally, they refer to themselves as "this" as though they were an instance.
Code sample:
public class EnumTest {
enum Seasons{
SUMMER,
FALL,
WINTER,
SPRING;
public String toString()
{
switch(this)
{
case SUMMER:
return "Hot Summer";
case FALL:
return "Colorful Fall";
case WINTER:
return "Crisp Winter";
case SPRING:
return "Allergy Season";
default
return "wth?";
}
}
}
public static void main(String[] args)
{
System.out.println(EnumTest.Seasons.SUMMER.toString());
}
}
Note how within toString() in the enum definition, there is a switch on "this".
Within the static method main, the Enum is accessed in typical static class manner.
I know that enums are a special type of class, but I'm still trying to understand the reasons for some of their unconventional quirks.
Is there some sort of Factory-pattern type of auto-construction going on when an enum constant is referenced? At exactly what point does it transition from being a static class to an instance?
Thanks!
The constants defined in the enum class are the only things that are implicitly static. It's close to (but not quite equivalent to):
public static final Seasons SUMMER = new Seasons();
public static final Seasons FALL = new Seasons();
public static final Seasons WINTER = new Seasons();
public static final Seasons SPRING = new Seasons();
This allows you to write code such as Seasons.SUMMER.
The rest of the class body is like a normal class body - public String toString() is not implicitly static, therefore it has access to this.
Think of the enum constants as statically declared objects like here:
class A {
public final static A FOO = new A ("FOO");
public final static A BAR = new A ("BAR");
private final String text;
private A(String text) {
this.text = text;
}
public String toString() {
return this.text;
}
}
Although statically declared, the objects for each of the constants can provide non-static methods you can call.

Is there a difference between initializing a variable with its declaration, or in a static initialization?

The static initializer is called once by the classloader, which is exactly what I want, but doing the initialization outside a static code block is more readable (debatable). Is there a difference between the two?
private static final Map<MyEnum, Cheese> cheeseCache;
static {
parserCache = new EnumMap< MyEnum, String>(MyEnum.class){{
for(MyEnum myEnum: MyEnum.values()){
put(myEnum, new Cheese(myEnum)) ;
}
}};
}
or this :
private static final Map<Lab, LabResultParser> cheeseCache
= new EnumMap< MyEnum, String>(MyEnum.class){{
for(MyEnum myEnum: MyEnum.values()){
put(myEnum, new Cheese(myEnum)) ;
}
}};
It can affect the ordering - for example you could have:
private static final int declaredFirst;
private static final int declaredSecond = new Random.nextInt();
static {
declaredFirst = declaredSecond + 1;
}
The initializers are executed in textual order. Of course, you could have just declared declaredFirst second:
private static final int declaredSecond = new Random.nextInt();
private static final int declaredFirst = declaredSecond + 1;
Personally I use static initialization blocks where I can't cleanly express the initial value in a single expression.
Oh, and if you initialize in a static initialization block, a variable can't be treated as a constant expression:
private static final int THIS_IS_A_CONSTANT = 10;
private static final int thisIsNotAConstant;
static {
thisIsNotAConstant = 20;
}
public static void main(String[] args) {
System.out.println(THIS_IS_A_CONSTANT); // 10 is inlined
System.out.println(thisIsNotAConstant); // 20 is not inlined
}
That's only rarely relevant, of course.
So in most cases it's just personal choice. Of course in your case, the ability to use more statements means you don't need to use the ugly (IMO) "anonymous inner class just to get some initialization":
private static final Map<MyEnum, Cheese> cheeseCache;
static {
parserCache = new EnumMap<>(MyEnum.class);
for (MyEnum myEnum: MyEnum.values()) {
put(myEnum, new Cheese(myEnum));
}
}
Both of your snippets create an anonyous inner class extending EnumMap just for the sake of initializing it. It would be cleaner to simply delegate to a method:
private static final Map<MyEnum, Cheese> CHEESE_CACHE = createCheeseCache();
private static Map<MyEnum, Cheese> createCheeseCache() {
EnumMap<MyEnum, Cheese> result = new EnumMap<MyEnum, Cheese>(MyEnum.class);
for (MyEnum myEnum: MyEnum.values()){
result.put(myEnum, new Cheese(myEnum)) ;
}
return result;
}
In your case there is no difference as you are not doing any logic to decide what value should be assigned to the static variable.
From the java tutorial:
Initialization value for a field in its declaration works well when
the initialization value is available and the initialization can be
put on one line. However, this form of initialization has limitations
because of its simplicity. If initialization requires some logic (for
example, error handling or a for loop to fill a complex array), simple
assignment is inadequate. Instance variables can be initialized in
constructors, where error handling or other logic can be used. To
provide the same capability for class variables, the Java programming
language includes static initialization blocks.
But I will go with the static block, because you will have an additional option to surround your initialization code in a try/catch if required. Assuming a scenario where something goes wrong while populating the enumMap, i may still want execution to proceed if the exception is not logically fatal.

How to initialise a Java enum using an inner static final field?

I am designing a text-only videogame with two characters not often seen together, yet very much alike in heart and disposition.
My problem is that I don't know how to initialise an enum constant through a constructor using a static final inner constant. Otherwise the game is good to go. ;)
Here's the dilemma:
The enum constants must be defined in the first line of the enum, if I am not mistaken
The first line can't refer to anything coming after it (i.e. "cannot reference a field before it is defined")
How do I resolve this catch-22?
Here some sample code released from the game under non-disclosure agreement:
enum ValiantHeroWithPrincessSavingTendencies {
SUPERMARIO(TYPICAL_QUOTE_FROM_MARIO), ZELDA(TYPICAL_QUOTE_FROM_ZELDA);
private String aPreparedQuotePurportedToBeSpontaneousAlmostImpulsive;
public String getQuoteUnderStressfulCircumstances() {
return aPreparedQuotePurportedToBeSpontaneousAlmostImpulsive;
}
private ValiantHeroWithPrincessSavingTendencies(String quote) {
aPreparedQuotePurportedToBeSpontaneousAlmostImpulsive = quote;
}
private static final String TYPICAL_QUOTE_FROM_ZELDA = "Have at ya!";
private static final String TYPICAL_QUOTE_FROM_MARIO = "We, wagliu'!";
}
I am trying to initialise SUPERMARIO using TYPICAL_QUOTE_FROM_MARIO but I haven't defined TYPICAL_QUOTE_FROM_MARIO yet. Moving the private static final field before SUPERMARIO is illegal, I think.
The only viable options are to either a) move your constants to another class or b) just put your constants directly into the value initializers.
If you move your constants, you can make the class a static class in the enum:
enum ValiantHeroWithPrincessSavingTendencies {
SUPERMARIO(Quotes.TYPICAL_QUOTE_FROM_MARIO),
ZELDA(Quotes.TYPICAL_QUOTE_FROM_ZELDA);
private String aPreparedQuotePurportedToBeSpontaneousAlmostImpulsive;
public String getQuoteUnderStressfulCircumstances() {
return aPreparedQuotePurportedToBeSpontaneousAlmostImpulsive;
}
private ValiantHeroWithPrincessSavingTendencies(String quote) {
aPreparedQuotePurportedToBeSpontaneousAlmostImpulsive = quote;
}
private static class Quotes {
private static final String TYPICAL_QUOTE_FROM_ZELDA = "Have at ya!";
private static final String TYPICAL_QUOTE_FROM_MARIO = "We, wagliu'!";
}
}
You can just access them via class name:
enum ValiantHeroWithPrincessSavingTendencies {
SUPERMARIO(ValiantHeroWithPrincessSavingTendencies.TYPICAL_QUOTE_FROM_MARIO),
ZELDA(ValiantHeroWithPrincessSavingTendencies.TYPICAL_QUOTE_FROM_ZELDA);
...
private static final String TYPICAL_QUOTE_FROM_ZELDA = "Have at ya!";
private static final String TYPICAL_QUOTE_FROM_MARIO = "We, wagliu'!";
}
It's simplier than Brian's solution
The private static final constants are local to the enum; just code them in the instance definitions. After that point they can be accessed internally from the aPreparedQuotePurportedToBeSpontaneousAlmostImpulsive variable.
You could always do something hacky like this:
public enum Derp
{
SOMETHING(),
SOMETHINGELSE();
private String herp;
public static final String A = "derp", B = "derp2";
public String getHerp()
{
return herp;
}
static
{
SOMETHING.herp = A;
SOMETHINGELSE.herp = B;
}
}

Categories