public class Test {
public static void main(String[] args) {
Platform1 p1=Platform1.FACEBOOK; //giving NullPointerException.
Platform2 p2=Platform2.FACEBOOK; //NO NPE why?
}
}
enum Platform1{
FACEBOOK,YOUTUBE,INSTAGRAM;
Platform1(){
initialize(this);
};
public void initialize(Platform1 platform){
switch (platform) {
//platform is not constructed yet,so getting `NPE`.
//ie. we doing something like -> switch (null) causing NPE.Fine!
case FACEBOOK:
System.out.println("THIS IS FACEBOOK");
break;
default:
break;
}
}
}
enum Platform2{
FACEBOOK("fb"),YOUTUBE("yt"),INSTAGRAM("ig");
private String displayName;
Platform2(String displayName){
this.displayName=displayName;
initialize(this);
};
public void initialize(Platform2 platform){
switch (platform.displayName) {
//platform not constructed,even No `NPE` & able to access its properties.
//switch (null.displayName) -> No Exception Why?
case "fb":
System.out.println("THIS IS FACEBOOK");
break;
default:
break;
}
}
}
Can anyone explain me why there is NullPointerException
in Platform1
but not in Platform2
. How in the second case we are able to access the enum object and its properties, even before the object is constructed?
You can't do that as you trying to work on the enum before it has been properly constucted. (As in the full thing constructed). You will notice that the error is trying to refer to the values part of the enum:
You need to allow the object to be properly internally intialised before working on it. This will work:
Obviously your initialise function should be renamed as it's just reporting the value. Your second example provides values and so works correctly.
From one of the Java docs:
You are getting a NPE because you are referencing an instance which hasn't been constructed yet.
Platform1.FACEBOOK
isnull
untilPlatform1
constructor which constructsFACEBOOK
instance is completed.The
Platform1
constructor callsinitialize
, which contains aswitch
. Thecase
in thatswitch
readsPlatform1.FACEBOOK
. SinceFACEBOOK
's constructor didn't return yet then theFACEBOOK
reference is null. Java Language Specification doesn't allownull
as acase
inswitch
, it will throw a runtime exception just as you found.As it has already been pointed out the
switch
on enums internally calls thevalues
method, but that would only be initialized after all enum constants were initialized:In the
Platform2
, this doesn't happen because theswitch
in on strings.A more object-oriented approach would be to create a
initialize
method that the constructor calls and is overridden by the constants that need a specialized initialization:The example below shows the initializing lifecycle:
In a nutshell, the enum constant was not initialized during building itself. In other words, the current enum instance will be assigned to the associated constant until the whole building work completed.
The switch statement will calling
values()
method, however the enum constants are in building and didn't ready for using. In order to avoid the client code to change its internal$VALUES
array, thevalues()
will clone its internal array, due to the enum constants is not ready yet then theNullPointerException
was thrown. here is the bytecodevalues()
method & static initializing block:Short Answer: the place where your calling initialize method, is getting called when that enum class is getting loaded by the class loader(in progress), and hence you can not access class level properties i.e static. Where as you can access the non-staic properties.
1. The constructor of enum gets called when you first time refer that Enum in code.
This line will Load the class for Enum Platform1 using the class loader. And the constructor will get called for each entry/instance in that enum, here it is 3.
Below code will print three hash codes.
So in short when this initialize method is getting called the Enum class is not fully loaded and it is in progress. And hence you cannot access any static property or method of that enum at that point of time.
2. When you use below line, it calls values() a static method,
Just change the static method to some equivalent non-static method and things will work. like,
3. So the answer to your question is, when Enum class is getting initialized, you
can not access any static things
but can access non static things
hence this below code is working gor you,
4. Here if you change the displayName to static, things will break.
Exactly. Just as @PeterS mentioned using enum before it has been properly constructed is causing NPE, because values() method is being called on un-constructed enum.
One more point, I would like to add here that
Platform1
andPlatform2
both are trying to use unconstructed enum in switch() but NPE is only inPlatform1
. Reason behind this is as follows :-Above piece of code from
Platform1
enum is usingplatform
enum object in switch where internally$SwitchMap$Platform1[]
array is used and to initialize this arrayvalues()
method is utilized, thus you get NPE. But inPlatform2
,switch (platform.displayName)
is comparison ondisplayName
which is already initialized and a string comparison occurs thus no NPE.Following are fragments of decompiled code :-
Platform1
Platform2