0

I got a enum class to store my message and I want to use a static method in it to put them to a hashmap.

But NullPointerException just happened. How can I fix it?

Msg.java

public enum Msg {

    //Common
    Message1("this"),
    Message2("is"),
    Message3("not"),
    Message4("cool");

    private String message;
    private static HashMap<Msg, String> Msgmap;
    
    private static HashMap<Msg, String> getMsgmap(){
        return Msgmap;
    }
    
    Msg(String msg) {
        this.message = msg;
        if(getMsgmap() == null) {
            setupMsgMap();
        }
    }

    private static void setupMessageMap() {
        Msgmap = new HashMap<Msg, String>();
        for(Msg msg :values()){
            Msgmap.put(msg, msg.message);
        }
    }
    
}

If I replace

for(Msg msg:values()){
            langmap.put(msg, msg.message);
        }

with

System.out.println(Message1.message);

will also throw NullPointerException

But if I try to print my enum strings in constructor. It will just works.

ItisCaleb
  • 3
  • 4

2 Answers2

1

If you can, i recommend you to use final immutable fields. This way you don't have to worry about thread safety.

Some information you should know:

  1. Enum constructor is called as many times as many constants the enum has.
  2. For enums which have at least one constant, it is impossible to write static block which will be called before constructor.
  3. In this case you can init static fields in static block.

Below is my version, based on @charlie-armstrong code:

enum Message { //the class is named "Message"
    Message1("this"),
    Message2("is"),
    Message3("not"),
    Message4("cool"); //line ends in a semicolon

    private static final HashMap<Message, String> Msgmap = new HashMap<>(); //Msgmap is initialized here

    static {
        for (Message msg : values()) { //loop through all the enum constants
            Msgmap.put(msg, msg.message); //add them all to the HashMap
        }
    }

    private final String message;

    Message(String msg) {
        this.message = msg;
        //I don't set up Msgmap in the constructor, because it is still initializing itself here, so we can't possibly add it to a HashMap yet
    }

    public static Map<Message, String> getMsgmap() {
        return Msgmap;
    }
}
Jakub Biały
  • 391
  • 2
  • 16
0

Okay, there are many issues with your code. I will first give you a commented version of your code, showing everything that's wrong, and then I will provide a version that does work. Here is your code:

public enum Msg { //should be called "Message", since it is in Message.java

    //Common
    Message1("this"),
    Message2("is"),
    Message3("not"),
    Message4("cool"), //syntax error, the line should end in a semicolon (;)

    private String message;
    private static HashMap<Msg, String> Msgmap; //Msgmap is never initialized
    
    private static HashMap<Msg, String> getMsgmap(){
        return Msgmap;
    }
    
    Msg(String msg) { //again, should be named "Message", not "Msg"
        this.message = msg;
        if(getMsgmap() == null) { //this will always be true, because Msgmap is never initialized
            setupMsgMap(); //this will run 4 times (once for every enum value)
        }
    }

    private static void setupMessageMap() {
        langmap = new HashMap<Lang, String>(); //syntax error (you didn't declare or infer a type), and the Lang class has not been provided to us, I assume you meant to use the Msg class instead
        for(Lang lang:values()){ //NullPointerException occurs here.  We're still initializing the first enum constant, but you're asking the compiler to loop through ALL the enum constants.  They aren't initialized yet though; they don't actually exist yet.
            langmap.put(lang, lang.lang);
        }
    }
    
}

Here's my updated code:

public enum Message { //the class is named "Message"
    Message1("this"),
    Message2("is"),
    Message3("not"),
    Message4("cool"); //line ends in a semicolon

    private String message;
    private static HashMap<Message, String> Msgmap = new HashMap<>(); //Msgmap is initialized here

    public static HashMap<Message, String> getMsgmap() {
        return Msgmap;
    }

    Message(String msg) {
        this.message = msg;
        //I don't set up Msgmap in the constructor, because it is still initializing itself here, so we can't possibly add it to a HashMap yet
    }

    public static void setupMsgMap() { //this will need to be called from outside AFTER initialization
        for (Message msg : values()) { //loop through all the enum constants
            Msgmap.put(msg, msg.message); //add them all to the HashMap
        }
    }
}
Charlie Armstrong
  • 2,332
  • 3
  • 13
  • 25
  • 1
    You can use a `static{}` block to do initialization of the map. – Polygnome Aug 04 '20 at 18:08
  • You can use it to initialize the map, but not to populate it, as the static block runs before the initializers. I just initialized it on the declaration line, either will work. – Charlie Armstrong Aug 04 '20 at 18:12
  • 1
    @polygnome is right. See my answer https://stackoverflow.com/a/63252960/12292000 or https://stackoverflow.com/a/11419587/12292000 . – Jakub Biały Aug 04 '20 at 18:26
  • @user12292000 Yeah you and polygnome are right... I had the initializers and static block the wrong way round in my head. Thank you. – Charlie Armstrong Aug 04 '20 at 18:35