I am studying the workings of Inner Class and inside its bytecode, I was tracing the stack and couldn't understand why is the getClass() called?
I found a similar question for Lambda function but couldn't understand it.
I did try to understand that is required for no null check, after JDK 8 it's been replaced by a static function called requiredNoNull.
Code :
class Outer{
class Inner{
}
public static void main(String args[]){
Outer.Inner obj = new Outer().new Inner();
}
}
ByteCode:
public static void main(java.lang.String[]);
Code:
0: new #2 // class Outer$Inner
3: dup
4: new #3 // class Outer
7: dup
8: invokespecial #4 // Method "<init>":()V
11: dup
12: invokevirtual #5 // Method java/lang/Object.getClass:()Ljava/lang/Class;
15: pop
16: invokespecial #6 // Method Outer$Inner."<init>":(LOuter;)V
19: astore_1
As far as I understand, @Eugene's answer is absolutely correct. I decided to add an explanation in simple words. Hopefully, it will help someone.
Answer: Calls to Object.getClass
were used by the compiler in JDK8 to generate NullPointerExceptions where necessary. In your example, this check is unnecessary, as new Outer()
can't be null, but the compiler wasn't smart enough to determine it.
In later versions of JDK, null checks were changed to use a more readable Objects.requireNotNull
. The compiler was also improved to optimize away redundant null checks.
Explanation:
Consider code like this:
class Outer{
class Inner{
}
public static void main(String args[]){
Outer.Inner obj = ((Outer) null).new Inner();
}
}
This code throws a NullPointerException, as it should.
The problem is, NPE is only logical from the Java point of view. Constructors do not exist on the byte code level. The compiler generates a byte code more or less equivalent to the following pseudocode:
class Outer {
public static void main(String[] args) {
Outer tmp = (Outer) null;
Outer$Inner obj = new; //object created
obj."<init>"(tmp);
}
}
class Outer$Inner {
//generated field
private final Outer outer;
//generated initializer
void "<init>"(Outer outer) {
this.outer = outer;
}
}
As you can see, the constructor was replaced with a method. And the method, by itself, will not check it's argument for null and, thus, will not throw an Exception.
For that reason, compiler has to add an additional null check to generate a NullPointerException
. Before Java 8, the quick and dirty way to achieve this was to emit a call to getClass
:
Outer tmp = (Outer) null;
tmp.getClass(); //generates an NPE
How can you check that this is, indeed, the reason:
Outer
class above using JDK 8.Object.getClass
from Outer.class
using any bytecode editor (e.g. JBE).