在 Java Lambda 中,为什么要对捕获的变量调用 getClass()
In Java Lambda's why is getClass() called on a captured variable
如果你看
的字节码
Consumer<String> println = System.out::println;
Java8更新121生成的字节码是
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
DUP
INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
POP
INVOKEDYNAMIC accept(Ljava/io/PrintStream;)Ljava/util/function/Consumer; [
// handle kind 0x6 : INVOKESTATIC
java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
// arguments:
(Ljava/lang/Object;)V,
// handle kind 0x5 : INVOKEVIRTUAL
java/io/PrintStream.println(Ljava/lang/String;)V,
(Ljava/lang/String;)V
]
ASTORE 1
正在 System.out
上调用 getClass()
方法,结果被忽略。
这是间接空引用检查吗?
当然,如果你 运行
PrintStream out = null;
Consumer<String> println = out::println;
这会触发 NullPointerException。
是的,调用 getClass()
已经成为一种规范的“测试 null
” 的习惯用法,因为 getClass()
被认为是一种廉价的内部操作,我想,HotSpot 可能是如果不使用 getClass()
的结果,则能够检测此模式并将操作减少为内部 null
-检查操作。
另一个例子是创建一个内部 class 实例和一个不是 this
:
的外部实例
public class ImplicitNullChecks {
class Inner {}
void createInner(ImplicitNullChecks obj) {
obj.new Inner();
}
void lambda(Object o) {
Supplier<String> s=o::toString;
}
}
编译为
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
}
另见 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.
这是否也适用于编程语言内部检查可能值得商榷,因为为此目的使用 Objects.requireNonNull
会创建对 java.lang
包外的 class 的依赖在源代码中不可见。在这种特定情况下,只有查看字节码的人才能看到技巧。但已决定更改 Java 9.
的行为
这就是 jdk1.9.0b160
编译相同测试的方式 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
}
如果你看
的字节码Consumer<String> println = System.out::println;
Java8更新121生成的字节码是
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
DUP
INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
POP
INVOKEDYNAMIC accept(Ljava/io/PrintStream;)Ljava/util/function/Consumer; [
// handle kind 0x6 : INVOKESTATIC
java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
// arguments:
(Ljava/lang/Object;)V,
// handle kind 0x5 : INVOKEVIRTUAL
java/io/PrintStream.println(Ljava/lang/String;)V,
(Ljava/lang/String;)V
]
ASTORE 1
正在 System.out
上调用 getClass()
方法,结果被忽略。
这是间接空引用检查吗?
当然,如果你 运行
PrintStream out = null;
Consumer<String> println = out::println;
这会触发 NullPointerException。
是的,调用 getClass()
已经成为一种规范的“测试 null
” 的习惯用法,因为 getClass()
被认为是一种廉价的内部操作,我想,HotSpot 可能是如果不使用 getClass()
的结果,则能够检测此模式并将操作减少为内部 null
-检查操作。
另一个例子是创建一个内部 class 实例和一个不是 this
:
public class ImplicitNullChecks {
class Inner {}
void createInner(ImplicitNullChecks obj) {
obj.new Inner();
}
void lambda(Object o) {
Supplier<String> s=o::toString;
}
}
编译为
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
}
另见 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.
这是否也适用于编程语言内部检查可能值得商榷,因为为此目的使用 Objects.requireNonNull
会创建对 java.lang
包外的 class 的依赖在源代码中不可见。在这种特定情况下,只有查看字节码的人才能看到技巧。但已决定更改 Java 9.
这就是 jdk1.9.0b160
编译相同测试的方式 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
}