0

I have read through the explanations of nullPointerExceptions and I know that it is when a value being pointed to has a null value as though it is referencing an object but I just cannot get my head around why I am getting one with an enum being used somewhat like a switch statement, but of course not actually using a switch statement. The expected behaviour, like I mentioned, should be like a switch statement. Perhaps I am missing a fundamental understanding of both the enum and NPE concepts. My code is as follows. (This code is for an Enigma cipher machine)

void displayMenu() {
    System.out.println("MAIN MENU");
    System.out.println();
    Stream.of(menuChoice.values()).map(stream -> stream.ordinal() + ". " + stream.msg).forEach(System.out::println);
    System.out.println();
}

public enum menuChoice {

    QUIT("Quit the Enigma", EnigmaMachine.instance.quitAction),
    ENCRPYT("Encrypt", EnigmaMachine.instance.encryptAction),
    DECRYPT("Decrypt", EnigmaMachine.instance.decryptAction);
    private String msg;
    public Runnable action;

    private menuChoice(String message, Runnable r) {
        this.msg = message;
        this.action = r;
    }
}

menuChoice getUserChoice() {
    System.out.print("Please enter your choice: ");
    int choice = s.nextInt();
    return menuChoice.values()[choice];
}

On the QUIT constant I am using a runnable that sets:

final Runnable quitAction = () -> {
    EnigmaMachine.instance.running = false;
};

The stacktrace is like this:

Exception in thread "main" java.lang.ExceptionInInitializerError
    at enigmamachine.EnigmaMachine.displayMenu(EnigmaMachine.java:24)
    at enigmamachine.EnigmaMachine.<init>(EnigmaMachine.java:85)
    at enigmamachine.EnigmaMachine.main(EnigmaMachine.java:91)
Caused by: java.lang.NullPointerException
    at enigmamachine.EnigmaMachine$menuChoice.<clinit>(EnigmaMachine.java:30)
    ... 3 more
Java Result: 1

My constructor is like this:

public EnigmaMachine() {
    this.running = true;
    while (this.running) {
        displayMenu();
        getUserChoice().action.run();
    }
}

Object instance is defined just above main function which is at the bottom of my code.

static EnigmaMachine instance;
public static void main(String[] args) {
    instance = new EnigmaMachine();
}

I am not necessarily looking for a solution, but perhaps a more relatable explanation of what I am dealing with. Many thanks.

  • 1
    What `EnigmaMachine.instance`, who initializes it and when does that happen? Well, I guess the answer to it won't "satisfy" me, because I'm sure that the initialization part will happen _after_ the classloader tries to load `menuChoice` and that's why it fails. – Tom Apr 16 '16 at 13:19
  • I have updated my question, if that is of any use. It is initiliased in the main function. – TooLateTheHero Apr 16 '16 at 13:28
  • 1
    *"if that is of any use*" of course it is :). *"It is initiliased in the main function."* And that is too late. In order to run your `main` method, the JVM needs to load the `EnigmaMachine` class and every static stuff inside of it. Since your enum is inside of it [(and implicitly static)](http://stackoverflow.com/questions/663834/in-java-are-enum-types-inside-a-class-static) it will be loaded as well and then initialized by using the provided `menuChoice` constructor. At that point `EnigmaMachine.instance.quitAction` can't be used, since `instance` is still `null`. – Tom Apr 16 '16 at 13:33
  • 1
    A java class constructor should be employed to instantiate an instance of an Object and nothing else. Your example constructor shown above performs functionality as well. Refactor you code so that the constructor provides the correct functionality, e.g. instantiates an Object. Then create a new method to provide the additional function you require – Hector Apr 16 '16 at 13:36
  • Many thanks, I have now solved the problem. – TooLateTheHero Apr 16 '16 at 13:41

1 Answers1

1

You get this issue simply because EnigmaMachine.instance is still null when it is call here QUIT("Quit the Enigma", EnigmaMachine.instance.quitAction). You should not call displayMenu() inside your constructor as it is too soon, you should call it a method to avoid this issue.

You could add for example a method start to your class as below:

    public void start() {
        this.running = true;
        while (this.running) {
            displayMenu();
            getUserChoice().action.run();
        }
    }

Then call it in the main method as next

    instance = new EnigmaMachine();
    instance.start();
Nicolas Filotto
  • 43,537
  • 11
  • 94
  • 122