web-dev-qa-db-fra.com

Java opérateur ternaire vs if / else en <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 durs.

REMARQUE: aucune expression ternaire pour conserver la compatibilité JDK <8 même lorsque vous utilisez le compilateur JDK 8 (en sélectionnant potentiellement Java.lang.reflect.Executable comme type commun, avec cette nouvelle classe de base non disponible sur les anciens JDK)

Quelle est la différence entre l'utilisation d'une expression ternaire et l'utilisation de if...else... construire dans ce contexte?

112
jddxf

Lorsque vous pensez au type des opérandes, le problème devient plus apparent:

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 aux deux this.method et this.constructor.

Dans Java 7 c'est Java.lang.reflect.Member , cependant la bibliothèque de classes Java 8 introduit un nouveau type Java.lang.reflect.Executable qui est plus spécialisé que le générique Member. 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é-version) du compilateur Java 8 semblent avoir produit 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 à son tour un ClassNotFoundException à l'exécution lors de l'exécution avec une bibliothèque de classes <JDK 8, car 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 depuis été corrigé, donc les deux if-else solution de contournement et le commentaire explicatif sont désormais obsolètes.

Remarque supplémentaire: On pourrait arriver à la conclusion que ce bogue du compilateur était présent avant Java 8. Cependant, l'octet le code généré pour le ternaire par OpenJDK 7 est le même que le code d'octet généré par OpenJDK 8. En fait, le type de l'expression est complètement non mentionné au moment de l'exécution, le code est vraiment seulement test, branch, load, return sans aucun contrôle supplémentaire Soyez donc assuré que ce n'est plus un problème et semble avoir été un problème temporaire lors du développement de Java 8.

102
dhke

Cela a été introduit dans assez ancien commit le 3 mai 2013, presque un an avant la sortie officielle de JDK-8. Le compilateur était en développement intensif à cette époque, de sorte que de tels problèmes de compatibilité pouvaient survenir. Je suppose que l'équipe Spring vient de tester la version JDK-8 et a essayé de résoudre les problèmes, même s'il s'agit en fait de problèmes de compilation. Avec la sortie officielle du JDK-8, cela est devenu sans objet. À présent, 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: du code qui peut être joliment compilé dans JDK-8 échoue avec JDK-9 javac. Je suppose que la plupart de ces problèmes seront résolus jusqu'à la sortie.

30
Tagir Valeev

La principale différence est qu'un bloc ifelse est un instruction tandis que le ternaire (plus souvent connu sous le nom de l'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. Un expression peut être utilisé dans une affectation:

int n = condition ? 3 : 2;

Ainsi, les deux expressions du ternaire après la condition doivent être contraignantes au même type. Cela peut provoquer des effets étranges dans Java en particulier avec la boxe automatique et le casting de référence automatique - c'est à cela que fait référence le commentaire dans votre code publié. La contrainte des expressions dans votre cas serait à un Java.lang.reflect.Executable type (comme c'est le type le plus spécialisé) et qui n'existe pas dans les anciennes versions de Java.

Sur le plan stylistique, vous devez utiliser un bloc ifelse si le code est de type instruction et un ternaire s'il est de type expression.

Bien sûr, vous pouvez faire en sorte qu'un bloc ifelse se comporte comme une expression si vous utilisez une fonction lambda.

7
Bathsheba

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 un casting n'aurait pas pu être écrit.

6
user207421