ejemplo - En Java Lambda, ¿por qué se llama getClass() a una variable capturada?
java stream collect to list (1)
Sí, llamar a getClass()
ha convertido en una expresión canónica de "prueba para null
", ya que se espera que getClass()
sea una operación intrínseca barata y, supongo, HotSpot podría detectar este patrón y reducir la operación a un null
intrínseco - verificar el funcionamiento, si el resultado de getClass()
no se utiliza.
Otro ejemplo es crear una instancia de clase interna con una instancia externa que no sea this
:
public class ImplicitNullChecks {
class Inner {}
void createInner(ImplicitNullChecks obj) {
obj.new Inner();
}
void lambda(Object o) {
Supplier<String> s=o::toString;
}
}
compila a
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
}
Ver también JDK-8073550 :
Algunos lugares en nuestra biblioteca de clase usan el extraño truco de usar object.getClass () para verificar la nulidad. Si bien esto parece un movimiento inteligente, en realidad confunde a las personas para que crean que esta es una práctica aprobada de verificación nula.
Con JDK 7, tenemos Objects.requireNonNull que proporciona la comprobación nula adecuada y declara el intento de forma adecuada.
Podría ser discutible si esto debería aplicarse también a las verificaciones intrínsecas del lenguaje de programación, ya que el uso de Objects.requireNonNull
para ese propósito crearía una dependencia a una clase fuera del paquete java.lang
no visible en el código fuente. Y en este caso específico, el truco solo es visible para quienes miran el código de bytes. Pero se ha decidido cambiar el comportamiento con Java 9.
Así es como jdk1.9.0b160
compila la misma clase de prueba:
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
}
Si miras el código de bytes para
Consumer<String> println = System.out::println;
el código de bytes generado por Java 8 actualización 121 es
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
Se getClass()
método getClass()
en System.out
y el resultado se ignora.
¿Es esto una verificación indirecta de referencia nula?
Ciertamente si corres
PrintStream out = null;
Consumer<String> println = out::println;
Esto desencadena una NullPointerException.