In Java Lambda's why is getClass() called on a captured variable

后端 未结 1 655
死守一世寂寞
死守一世寂寞 2020-11-30 04:04

If you look at the byte code for

Consumer println = System.out::println;

the byte code generates by Java 8 update 121 is

相关标签:
1条回答
  • 2020-11-30 04:34

    Yes, calling getClass() has become a canonical “test for null” idiom, as getClass() is expected to be a cheap intrinsic operation and, I suppose, HotSpot might be capable of detecting this pattern and reduce the operation to an intrinsic null-check operation, if the result of getClass() is not used.

    Another example is creating an inner class instance with an outer instance that is not this:

    public class ImplicitNullChecks {
        class Inner {}
        void createInner(ImplicitNullChecks obj) {
            obj.new Inner();
        }
    
        void lambda(Object o) {
            Supplier<String> s=o::toString;
        }
    }
    

    compiles to

    Compiled from "ImplicitNullChecks.java"
    public class bytecodetests.ImplicitNullChecks {
      public bytecodetests.ImplicitNullChecks();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
           4: return
    
      void createInner(bytecodetests.ImplicitNullChecks);
        Code:
           0: new           #23                 // class bytecodetests/ImplicitNullChecks$Inner
           3: dup
           4: aload_1
           5: dup
           6: invokevirtual #24                 // Method java/lang/Object.getClass:()Ljava/lang/Class;
           9: pop
          10: invokespecial #25                 // Method bytecodetests/ImplicitNullChecks$Inner."<init>":(Lbytecodetests/ImplicitNullChecks;)V
          13: pop
          14: return
    
      void lambda(java.lang.Object);
        Code:
           0: aload_1
           1: dup
           2: invokevirtual #24                 // Method java/lang/Object.getClass:()Ljava/lang/Class;
           5: pop
           6: invokedynamic #26,  0             // InvokeDynamic #0:get:(Ljava/lang/Object;)Ljava/util/function/Supplier;
          11: astore_2
          12: return
    }
    

    See also JDK-8073550:

    A few places in our class library use the weird trick of using object.getClass() to check for nullity. While this make seem a smart move, it actually confuses people into believing this is an approved practice of null checking.

    With JDK 7, we have Objects.requireNonNull that provide the proper null checking, and declare the intent properly.

    It might be debatable whether this should apply to programming language intrinsic checks as well, as using Objects.requireNonNull for that purpose would create a dependency to a class outside the java.lang package not visible in the source code. And in this specific case, the trick is only visible to those who look at the byte code. But it has been decided to change the behavior with Java 9.

    This is how jdk1.9.0b160 compiles the same test class:

    Compiled from "ImplicitNullChecks.java"
    public class bytecodetests.ImplicitNullChecks {
      public bytecodetests.ImplicitNullChecks();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
           4: return
    
      void createInner(bytecodetests.ImplicitNullChecks);
        Code:
           0: new           #26                 // class bytecodetests/ImplicitNullChecks$Inner
           3: dup
           4: aload_1
           5: dup
           6: invokestatic  #27                 // Method java/util/Objects.requireNonNull:(Ljava/lang/Object;)Ljava/lang/Object;
           9: pop
          10: invokespecial #28                 // Method bytecodetests/ImplicitNullChecks$Inner."<init>":(Lbytecodetests/ImplicitNullChecks;)V
          13: pop
          14: return
    
      void lambda(java.lang.Object);
        Code:
           0: aload_1
           1: dup
           2: invokestatic  #27                 // Method java/util/Objects.requireNonNull:(Ljava/lang/Object;)Ljava/lang/Object;
           5: pop
           6: invokedynamic #29,  0             // InvokeDynamic #0:get:(Ljava/lang/Object;)Ljava/util/function/Supplier;
          11: astore_2
          12: return
    }
    
    0 讨论(0)
提交回复
热议问题