34 votes

Dans les Lambda Java, pourquoi la fonction getClass() est-elle appelée sur une variable capturée ?

Si vous regardez le code de l'octet pour

Consumer<String> println = System.out::println;

le byte code généré par la mise à jour 121 de Java 8 est le suivant

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

En getClass() est appelée sur la méthode System.out et le résultat est ignoré.

S'agit-il d'un contrôle indirect de la référence nulle ?

Il est certain que si vous exécutez

PrintStream out = null;
Consumer<String> println = out::println;

Cela déclenche une NullPointerException.

31voto

Holger Points 13789

Oui, en appelant getClass() est devenu un "test" canonique pour les null idiome ", comme getClass() est censée être une opération intrinsèque peu coûteuse et, je suppose, HotSpot pourrait être capable de détecter ce modèle et de réduire l'opération à une opération intrinsèque. null -si le résultat de l'opération getClass() n'est pas utilisé.

Un autre exemple est la création d'une instance de classe interne avec une instance externe qui n'est pas this :

public class ImplicitNullChecks {
    class Inner {}
    void createInner(ImplicitNullChecks obj) {
        obj.new Inner();
    }

    void lambda(Object o) {
        Supplier<String> s=o::toString;
    }
}

se compile en

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
}

Voir aussi JDK-8073550 :

Dans notre bibliothèque de classes, il y a quelques endroits où l'on utilise l'astuce bizarre d'utiliser object.getClass() pour vérifier la nullité d'un objet. Bien que cela puisse sembler être une démarche intelligente, il s'agit en fait d'une confusion qui fait croire aux gens qu'il s'agit d'une pratique approuvée. pratique approuvée de vérification de la nullité.

Avec le JDK 7, nous avons Objects.requireNonNull qui fournit la vérification de la nullité et déclare l'intention correcte. correctement.

On peut se demander si cela ne devrait pas s'appliquer également aux contrôles intrinsèques des langages de programmation, étant donné que l'utilisation de Objects.requireNonNull à cette fin créerait une dépendance à l'égard d'une classe extérieure à la classe java.lang n'est pas visible dans le code source. Et dans ce cas précis, l'astuce n'est visible que pour ceux qui regardent le code byte. Mais il a été décidé de changer ce comportement avec Java 9.

C'est ainsi que jdk1.9.0b160 compile la même classe de test :

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
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X