4

I have a program below:

package com.company;

enum Color {
    RED, GREEN;

    Color() {
        System.out.println(Main.getRegionName(this));
    }
}

public class Main {
    public static String getRegionName(Color region) {
        switch (region) {
            case RED:
                return "red";
            case GREEN:
                return "green";
            default:
                return "false";
        }
    }

    public static void main(String[] args) {
        Main m = new Main();
        Color color = Color.RED;
    }
}

When I run the program, I got the exceptions below:

Exception in thread "main" java.lang.ExceptionInInitializerError
    at com.company.Main.getRegionName(Main.java:13)
    at com.company.Color.<init>(Main.java:7)
    at com.company.Color.<clinit>(Main.java:4)
    at com.company.Main.main(Main.java:25)
Caused by: java.lang.NullPointerException
    at com.company.Color.values(Main.java:3)
    at com.company.Main$1.<clinit>(Main.java:13)
    ... 4 more

What's the reason for it? Is the 'this' initialized for the Color class when it calls Main.getRegionName(this) in its constructor?

injoy
  • 3,993
  • 10
  • 40
  • 69

2 Answers2

8

The execution of the code can be described like this:

  • Class Loader loads the enum Color.
  • It calls the constructor of Color for the first value, RED.
  • In the constructor, there's a call to method Main#getRegionName.
  • In method Main#getRegionName, the switch will call to the Color#values to obtain the values of the enum for the switch.
  • Since Color values have not been loaded yet, it breaks by a NullPointerException, and the exception gets propagated.

This behavior is noticed by this line in the stacktrace:

Caused by: java.lang.NullPointerException
at com.company.Color.values(Main.java:3)

More info:

Community
  • 1
  • 1
Luiggi Mendoza
  • 85,076
  • 16
  • 154
  • 332
  • But we can get access to some internal attributes using 'this' pointer? Like: Color(int id) {this.id = id;} ? – injoy May 01 '15 at 23:06
  • @injoy this approach seems very odd to begin with, and shouldn't be used. Why a field of an enum must rely on the values of the enum **when creating the values of the enum**? – Luiggi Mendoza May 01 '15 at 23:08
  • @injoy of course you can, otherwise what would be the point of attributes in enumerations – Dici May 01 '15 at 23:08
  • I don't think `values` does return `null`. Just writing `values()` in an enum constructor throws this exception. Nothing is returned. – Paul Boddington May 01 '15 at 23:09
  • @LuiggiMendoza don't understand your comment. It is pretty common to have attributes on an enumeration – Dici May 01 '15 at 23:09
  • @pbabcdefp Most probably, `values` is not null, but its elements are, qt the loading of the enum – Dici May 01 '15 at 23:12
  • @Dici the problem is not related to having fields in the enum, but how you initialize them. You should not initialize the fields of the enum in the constructor and depending on the other values of the enum for it. It may be better passing arguments to the constructor rather than using this approach. – Luiggi Mendoza May 01 '15 at 23:13
  • @LuiggiMendoza My final confusion is why we can call something like this.id = 10; (if there exists an id field) but we cannot call this.values() in the enum constructor? – injoy May 01 '15 at 23:17
  • @pbabcdefp my answer points to that Q/A now. And seems like it depends on how the compiler adds this. But if I were wrong and you were right, the application would have crashed even before and didn't throw a NPE. – Luiggi Mendoza May 01 '15 at 23:23
  • @injoy `Enum#values` expects that all the values of the enums are rightly initialized. Calling `values` when still initializing the values of the `Enum` will break this behavior. – Luiggi Mendoza May 01 '15 at 23:27
  • @LuiggiMendoza where do you initiaize your attributes, if not in a constructor ? By the way, `Color(int id) {this.id = id;}` is not initializing depending on the other values of the enum (the OP is). I know it is not the question, but what's wrong with this ? http://pastebin.com/41iWiuwd I find this code perfectly fine and clear. – Dici May 02 '15 at 10:19
  • @LuiggiMendoza Or maybe by *fields of the enum* you mean `RED` and `GREEN` themselves and not their attributes – Dici May 02 '15 at 10:28
1

You shouldn't access the object you are constructing from inside the constructor System.out.println(Main.getRegionName(this));

The 'this' pointer is not initialized while you are inside the constructor.

Roberto
  • 2,115
  • 24
  • 32