Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enum of Enum is NULL

I'm developing a LALG compiler to my college course on Java 1.6. So I did a types class and grammar class.

EnumTypes

public enum EnumTypes {

    A("OLA"),
    B("MUNDO"),
    C("HELLO"),
    D("WORLD"),

    /**
     * The order below is reversed on purpose.
     * Revert it and will you get a NULL list of types furder.
     */

    I(EnumGrammar.THREE),
    H(EnumGrammar.TWO),
    F(EnumGrammar.ONE),
    E(EnumGrammar.ZERO);

    private String strValue;
    private EnumGrammar enumGrammarValue;

    private EnumTypes(String strValue) {
        this.strValue = strValue;
    }

    private EnumTypes(EnumGrammar enumGrammarValue) {
        this.enumGrammarValue = enumGrammarValue;
    }

    public String getStrValue() {
        return strValue;
    }

    public EnumGrammar getEnumTiposValue() {
        return enumGrammarValue;
    }
}

EnumGrammar

public enum EnumGrammar {

    ZERO(EnumTypes.A,EnumTypes.B,EnumTypes.F,EnumTypes.D),
    ONE(EnumTypes.C),
    TWO(EnumTypes.B,EnumTypes.H),
    THREE(EnumTypes.D,EnumTypes.A,EnumTypes.C);

    private EnumTypes[] values;

    private EnumGrammar(EnumTypes ... values) {
        this.values = values;
    }

    public EnumTypes[] getValues() {
        return values;
    }
}

When I call EnumTypes.E.getEnumTiposValue().getValues() where are supposed to be the EnumTypes.F value is NULL.

Main

public class Main {

    public static void main(String[] args) {
        //prints [A, B, null, D]
        System.out.println(Arrays.toString(EnumTypes.E.getEnumTiposValue().getValues()));
    }

}

There are a workaround or something like that?

Thanks!

like image 272
Idemax Avatar asked Jun 06 '13 20:06

Idemax


People also ask

Can an enum be null?

An enum can be null. When an enum (like Color in this program) is a field of a class, it is by default set to null.

How do you check if an enum is null?

It is an Object representing a defined state. Think of it like an static final Object which can't be changed after intialization, but easyly compared. So what you can do is check on null and on Equals to your existing Enum Values.

Can enum be null in C#?

An enum value cannot be null. It is a value type like an int. To avoid the "cannot convert null" error, use a special None constant as the first enum item.

Can I pass null in enum Java?

If you want to represent null with an enum, then you'll have to explicit this by using a null object pattern manually with a NONE value and a custom implementation for prettyPrint() based on NONE value.


2 Answers

Essentially, it is always a very risky thing to allow a reference to an object to get outside of the class before the class is fully constructed, that is before the constructor is finished. Enums are singletons. Here you have two classes whose constructors receive each other's instances in a circular dependency. Add to this that class loading is lazy, so the classes will be loaded and enum instances created as you go and it sounds quite reasonable that the ends result depends on the order in which the enums are initialized.

I can't quote the corresponding point from JLS right now (I'll look for it), but I believe that if you allow a reference to an object to "leave the class" from outside of the constructor (which happens here due to enums being singletons initialized by the JVM), the JVM is free to do something strange.

EDIT: these points from the JLS are of importance for the case:

  • 17.5.2 - A read of a final field of an object within the thread that constructs that object is ordered with respect to the initialization of that field within the constructor by the usual happens-before rules. If the read occurs after the field is set in the constructor, it sees the value the final field is assigned, otherwise it sees the default value. Since enum values are internally treated like static final fields (see 16.5 below), if you reference one enum from inside the constructor of another enum whose constructor references the first one, at least one of these two objects will not yet have been fully initialized and so the reference may still be null at this point.
  • 16.5 - The definite assignment/unassignment status of any construct within the class body of an enum constant is governed by the usual rules for classes
  • 8.3.2 - rules for initialization of fields
  • 12.4.1 - when initialization occurs
like image 56
Michał Kosmulski Avatar answered Oct 23 '22 10:10

Michał Kosmulski


Here is what's happening, in order:

  1. Your code calls EnumTypes.E.getEnumTiposValue(), triggering class loading of EnumTypes.
  2. Static initialization of EnumTypes begins - its enum constants will be initialized in the order they're declared.
  3. EnumTypes.A through EnumTypes.D are initialized.
  4. EnumTypes.I begins initialization - its constructor call references EnumGrammar.THREE, triggering class loading of EnumGrammar.
  5. Static initialization of EnumGrammar begins - its enum constants will be initialized in the order they're declared.
  6. EnumGrammar.ZERO is initialized - its constructor call references EnumTypes.A, EnumTypes.B, EnumTypes.F, and EnumTypes.D. Out of those, EnumTypes.F has not yet been initialized. Therefore, the reference to it is null.

From there, static initialization of the two enum classes finishes, but it doesn't matter for EnumGrammar.ZERO - its values field has already been set.

like image 27
Paul Bellora Avatar answered Oct 23 '22 09:10

Paul Bellora