Why is getClass() called when we create an object for Inner class?
Asked Answered
A

2

9

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
Ampulla answered 4/7, 2019 at 6:24 Comment(3)
The null check may be all there is to it. That's a method that's available on all objects, but which can't be overridden (so the compiler knows it's fast). I'm not sure, though.Beverle
That “similar question for Lambda function” is this one. When you say, you “did try to understand that is required for no null check”, than you should also say at which point you couldn’t understand it. The question, as it is now, looks entirely redundant.Lacuna
@Lacuna perfect! I was wondering where I remember that from...Biogeography
H
4

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:

  1. Compile the Outer class above using JDK 8.
  2. Run it, it should throw an NPE.
  3. Remove the call to Object.getClass from Outer.class using any bytecode editor (e.g. JBE).
  4. Run the program again, it should complete successfully.
Herrah answered 4/7, 2019 at 9:39 Comment(0)
B
5

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.

Biogeography answered 4/7, 2019 at 6:58 Comment(7)
@Ampulla String s = null; s.getClass() what will this throw? the result of s.getClass() is simply ignored in this caseBiogeography
In context to the given question, What object does it take for calling getClass()? and the returned class is pushed in the operand stack or not? Because normally for a null reference getClass() throws an ExceptionAmpulla
Would you mind expanding this a little bit for us mere mortals? :) What exactly is being checked for null here?Herrah
@Biogeography Thank you! Turns out javac in JDK8 isn't smart enough to optimize the unnecessary call to getClass.Herrah
The question has been deleted. Also @Biogeography Now if i consider the return getClass() in the operand stack, it messes up the flow. Would you please explain the Stack Trace in the given ByteCode of the question?Ampulla
@Ampulla would you explain your understanding of "messing up the flow"?Biogeography
It's solved! Thank you so much. ( i meant stack was not being empty at the end of method, but it was my mistake.)Ampulla
H
4

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:

  1. Compile the Outer class above using JDK 8.
  2. Run it, it should throw an NPE.
  3. Remove the call to Object.getClass from Outer.class using any bytecode editor (e.g. JBE).
  4. Run the program again, it should complete successfully.
Herrah answered 4/7, 2019 at 9:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.