Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this enum compile?

Tags:

java

enums

I wanted to create an enum where each constant has a Map associated with it. I accomplished this by giving each constant an instance initializer, like so:

import java.util.HashMap;
import java.util.Map;

public enum Derp {
    FOO {{
            mMap.put("bar", 1);
        }};

    // cannot be private
    protected final Map<String, Integer> mMap = new HashMap<>();
}

I found that if mMap is private, it cannot be referenced in the instance initializer. The error is Cannot make a static reference to the non-static field mMap. Before the reason for this occurred to me, I consulted JLS §8.9.2, which says in part:

It is a compile-time error for the constructors, instance initializer blocks, or instance variable initializer expressions of an enum constant e to refer to e or to an enum constant of the same type that is declared to the right of e.

Aren't I breaking this rule by implicitly referencing FOO in FOO's own instance intializer? How does this compile? It not only compiles, but works correctly at runtime.

(It occurred to me that mMap cannot be private because I'm implicitly creating an anonymous subclass which cannot reference a private field in its superclass. Which is a bit weird in itself since enums are implicitly final...)

like image 550
Kevin Krumwiede Avatar asked Apr 15 '15 03:04

Kevin Krumwiede


People also ask

What do enums compile to?

Java enums are syntactic sugar. Java compiler compiles an enum class to a simple Java class extending java.

Are enums compile time?

Enum values or Enum instances are public, static, and final in Java. They are a compile-time constant, which means you can not changes values of Enum instances and further assignment will result in a compile-time error.

Are enums evaluated at compile time?

The use of enum here is guaranteed to occupy no storage in the object, and the enumerators are all evaluated at compile time. You can also explicitly establish the values of the enumerators: enum { one = 1, two = 2, three };

What is the purpose of an enum?

An enumeration, or Enum , is a symbolic name for a set of values. Enumerations are treated as data types, and you can use them to create sets of constants for use with variables and properties.


1 Answers

It is a compile-time error for the constructors, instance initializer blocks, or instance variable initializer expressions of an enum constant e to refer to e or to an enum constant of the same type that is declared to the right of e.

The specification here just means you can't reference by name because the field referred to by e is not initialized yet. It doesn't mean you can't access this.

It's basically the same rule as any other initializer (like int x = x;).

We can see why with an example like (Ideone):

enum Example {
    INSTANCE {{
        subversion();
    }};

    static void subversion() {
        System.out.println(INSTANCE);
    }

    public static void main(String[] args) {
        System.out.println(INSTANCE);
    }
}

Which outputs

null
INSTANCE

I found that if mMap is private, it cannot be referenced in the instance initializer.

You can qualify the call as super.mMap.put(...);. The private mMap is not inherited, but it's accessible from the inner class. I also covered this here. The short of it is that the simple name mMap refers to a non-existent outer instance of Derp.

We can verify this is the case with an example like (Ideone):

class Example {
    private int x;

    class Inner extends Example {{
        x = 1;       // refers to the outer instance
        super.x = 2; // refers to the inner instance
    }}

    public static void main(String[] args) {
        Example outer = new Example();
        Example inner = outer.new Inner();
        System.out.println(outer.x); // prints 1
        System.out.println(inner.x); // prints 2
    }
}

Except in your case FOO is static so there is no outer instance—hence compiler error.

like image 66
Radiodef Avatar answered Sep 22 '22 04:09

Radiodef