Opérateur ternaire Java vs if / else in

récemment je suis en train de lire le code source de Spring Framework. Quelque chose que je ne peux pas comprendre 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 appartient à la classe org.springframework.core.MethodParameter . Le code est facile à comprendre tandis que les commentaires sont durs.

NOTE: 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, avec cette nouvelle classe de base non disponible sur âgées Jdk)

Quelle est la différence entre utiliser l'expression ternaire et utiliser la construction if...else... dans ce contexte?

112
demandé sur Shayan Ghosh 2015-09-09 11:56:07

4 réponses

quand on pense au type des opérandes, le problème devient plus apparent:

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

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

en Java 7 c'est java.lang.reflect.Member , cependant la bibliothèque de classe Java 8 introduit un nouveau type java.lang.reflect.Executable qui est plus spécialisé que le générique Member . Ainsi, avec une bibliothèque de classe Java 8, le type de résultat de l'expression ternaire est Executable plutôt que Member .

certaines versions (pré-release) 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 une charge de classe, et donc à son tour un ClassNotFoundException à l'exécution avec une bibliothèque de classe < JDK 8, parce que Executable n'existe que pour JDK ≥ 8.

comme L'a noté Tagir Valeev dans cette réponse , il s'agit en fait d'un bug dans les versions pré-publication de JDK 8 et a depuis été corrigé, de sorte que la solution de contournement if-else et le commentaire explicatif sont désormais obsolètes.

note supplémentaire: on pourrait conclure que ce bogue de compilateur était présent avant Java 8. Cependant, le code octet généré pour le ternaire par OpenJDK 7 est le même que le code octet généré par OpenJDK 8. En fait, le type d'expression passe complètement inaperçu à l'exécution, le code n'est en réalité que test, branche, charge, retour sans aucun contrôle supplémentaire en cours. Ainsi, soyez assurés que ce n'est plus un problème (désormais) et semble en effet avoir été un problème temporaire lors du développement de Java 8.

102
répondu dhke 2017-05-23 10:29:27

cela a été introduit dans tout à fait vieux commit le 3 mai 2013, presque un an avant la sortie officielle de JDK-8. Le compilateur était alors en cours de développement, de sorte que de tels problèmes de compatibilité pouvaient se produire. Je suppose que L'équipe de Spring vient de tester la construction JDK-8 et a essayé de résoudre des problèmes, même s'ils sont en fait des problèmes de compilateur. Par JDK-8 publication officielle ceci est devenu hors de propos. Maintenant, l'opérateur ternaire dans ce code fonctionne comme prévu (pas de référence à Executable class dans compilé .classe-fichier est présent).

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

30
répondu Tagir Valeev 2015-09-09 10:57:51
la principale différence est qu'un bloc if else est un bloc alors que le ternaire (plus connu sous le nom d'opérateur conditionnel en Java) est une expression .

Un déclaration peut faire des choses comme return à l'appelant sur certains chemins de contrôle. Une expression peut être utilisée dans une cession:

int n = condition ? 3 : 2;

ainsi les deux expressions dans le ternaire après la condition doivent être coercables au même type. Cela peut causer des effets étranges en Java, en particulier avec l'auto-boxe et le casting de référence automatique - c'est ce à quoi se réfère le commentaire dans votre code posté. La coercition des expressions dans votre cas serait à un java.lang.reflect.Executable type (comme c'est le type le plus spécialisé ) et qui ne existe dans les anciennes versions de Java.

de façon stylistique, vous devez utiliser un bloc if else si le code est semblable à une instruction, et un ternaire s'il est semblable à une expression.

bien sûr, vous pouvez faire un bloc if else se comporter comme une expression si vous utilisez une fonction lambda.

7
répondu Bathsheba 2015-09-09 11:50:25

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

difficile de voir pourquoi un plâtre n'a pas pu être écrit.

6
répondu user207421 2015-09-09 10:15:18