Opérateur ternaire Java vs if / else dans <compatibilité JDK8

Récemment, je lis le code source de Spring Framework. Quelque chose que je ne comprends pas va ici:

public Member getMember() { // NOTE: no ternary expression to retain JDK <8 compatibility even when using // the JDK 8 compiler (potentially selecting java.lang.reflect.Executable // as common type, with that new base class not available on older JDKs) if (this.method != null) { return this.method; } else { return this.constructor; } } 

Cette méthode est membre de la classe org.springframework.core.MethodParameter . Le code est facile à comprendre alors que les commentaires sont difficiles.

REMARQUE: pas d’expression ternaire pour conserver la compatibilité JDK <8, même en utilisant le compilateur JDK 8 (en sélectionnant potentiellement java.lang.reflect.Executable comme type commun, cette nouvelle classe de base n’étant pas disponible sur les anciens JDK)

Quelle est la différence entre utiliser une expression ternaire et utiliser if...else... construire dans ce contexte?

Lorsque vous pensez au type d’opérandes, le problème devient plus évident:

 this.method != null ? this.method : this.constructor 

a pour type le type commun le plus spécialisé des deux opérandes, c’est-à-dire le type le plus spécialisé commun à this.method et à this.constructor .

Dans Java 7, il s’agit de java.lang.reflect.Member , mais la bibliothèque de classes Java 8 introduit un nouveau type java.lang.reflect.Executable qui est plus spécialisé que le Member générique. Par conséquent, avec une bibliothèque de classes Java 8, le type de résultat de l’expression ternaire est Executable plutôt que Member .

Certaines versions (préalables) du compilateur Java 8 semblent avoir généré une référence explicite à Executable dans le code généré lors de la compilation de l’opérateur ternaire. Cela déclencherait un chargement de classe, et donc une ClassNotFoundException à l’exécution lors de l’exécution avec une bibliothèque de classes Executable n’existe que pour JDK ≥ 8.

Comme l’a noté Tagir Valeev dans cette réponse , il s’agit en fait d’un bogue dans les versions préliminaires de JDK 8 et a été corrigé depuis. La solution de contournement if-else et le commentaire explicatif sont désormais obsolètes.

Note complémentaire: On pourrait en arriver à la conclusion que ce bogue de compilateur était présent avant Java 8. Cependant, le code d’octet généré par OpenJDK 7 pour le ternaire est identique au code d’octet généré par OpenJDK 8. En fait, le type de expression passe complètement sous silence au moment de l’exécution, le code n’est vraiment que test, twig, charge, retour sans vérification supplémentaire. Soyez donc assuré que ce n’est plus un problème (et qu’il semble en effet avoir été un problème temporaire lors du développement de Java 8).

Cela a été introduit dans le vieux commit au 3 mai 2013, presque un an avant la sortie officielle de JDK-8. Le compilateur était en plein développement ces temps-là, de tels problèmes de compatibilité pouvaient donc se produire. Je suppose que l’équipe de Spring a juste testé la version JDK-8 et essayé de résoudre les problèmes, même s’il s’agit en fait de problèmes de compilation. Par la publication officielle de JDK-8, cela devenait inutile. Maintenant, l’opérateur ternaire dans ce code fonctionne correctement comme prévu (aucune référence à la classe Executable dans le fichier .class compilé n’est présente).

Actuellement, des choses similaires apparaissent dans JDK-9: un code qui peut être joliment compilé dans JDK-8 a échoué avec JDK-9 javac. Je suppose que la plupart de ces problèmes seront résolus jusqu’à la sortie.

La principale différence est qu’un bloc if else est une déclaration, tandis que le ternaire (plus souvent appelé opérateur conditionnel en Java) est une expression .

Une instruction peut faire des choses comme return à l’appelant sur certains des chemins de contrôle. Une expression peut être utilisée dans une affectation:

int n = condition ? 3 : 2;

Donc, les deux expressions dans le ternaire après la condition doivent être coercibles au même type. Cela peut provoquer des effets bizarres en Java, en particulier avec la mise en boîte automatique et la diffusion de référence automatique – c’est ce à quoi fait référence le commentaire dans votre code. La contrainte des expressions dans votre cas serait un type java.lang.reflect.Executable (comme c’est le type le plus spécialisé ) et qui n’existe pas dans les anciennes versions de Java.

Du sharepoint vue stylistique, vous devez utiliser un bloc if else si le code ressemble à une instruction, et un ternaire si c’est une expression.

Bien sûr, vous pouvez créer un bloc if else comme une expression si vous utilisez une fonction lambda.

Le type de valeur de retour dans une expression ternaire est affecté par les classes parentes, qui ont changé comme décrit dans Java 8.

Difficile de voir pourquoi une dissortingbution n’aurait pas pu être écrite.