Why is getClass() called when we create an object for Inner class?
It's a null check in disguise, nothing more, nothing less. Though, in that particular case it's not really needed and future javac
optimize that a little - look at the example below.
May be this will explain the issue better (using java-12, where this getClass
hack has been replaced by Objects::requireNonNull
):
public class Outer {
class Inner {
}
public void left() {
Outer.Inner inner = new Outer().new Inner();
}
public void right(Outer outer) {
Outer.Inner inner = outer.new Inner();
}
}
left
method will compile to something (you can look at the byte code yourself) that will not use Objects::requireNonNull
, since the creation of Outer
happens in place and the compiler can for sure tell that new Outer()
instance is not null
.
On the other hand of you pass Outer
as a parameter, the compiler can't prove that the instance passed is not null for sure, thus Objects::requireNonNull
will be present in the byte code.
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:
- Compile the
Outer
class above using JDK 8. - Run it, it should throw an NPE.
- Remove the call to
Object.getClass
fromOuter.class
using any bytecode editor (e.g. JBE). - Run the program again, it should complete successfully.