web-dev-qa-db-ja.com

Java三項演算子vs <JDK8互換性のif / else

最近、私はSpring Frameworkのソースコードを読んでいます。私が理解できないことはここに行きます:

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;
    }
}

このメソッドは、org.springframework.core.MethodParameterクラスのメンバーです。コードはわかりやすく、コメントは難しいです。

注:JDK 8コンパイラを使用する場合でも、JDK <8互換性を保持するための3項式はありません(潜在的にJava.lang.reflect.Executableを選択し、新しいベースクラスは古いJDKでは使用できません)

このコンテキストで三項式を使用することとif...else...コンストラクトを使用することの違いは何ですか?

112
jddxf

オペランドのタイプを考えると、問題がより明確になります。

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

型として、両方のオペランドの最も特殊な共通型、つまり両方のthis.methodおよびthis.constructor

Java 7これは Java.lang.reflect.Member 、ただしJava 8クラスライブラリは新しいタイプを導入します Java.lang.reflect.Executable これは、一般的なMemberよりも特殊化されています。したがって、Java 8クラスライブラリを使用すると、3項式の結果タイプはExecutableではなくMemberになります。

Java 8コンパイラの一部の(プレリリース)バージョンは、三項演算子のコンパイル時に生成コード内でExecutableへの明示的な参照を生成したようです。これにより、クラスのロードがトリガーされます。 、したがって、ClassNotFoundExceptionは8以上のJDKに対してのみ存在するため、クラスライブラリ<JDK 8で実行する場合、実行時にExecutableが順番になります。

この回答 でTagir Valeevが指摘したように、これは実際にはJDK 8のプレリリースバージョンのバグであり、その後修正されているため、if-else回避策と説明コメントは廃止されました。

追記:このコンパイラのバグは、Java 8.より前に存在していたという結論に達するかもしれません。ただし、バイトOpenJDK 7によって生成された3進コードは、OpenJDK 8によって生成されたバイトコードと同じです。実際、式のタイプは実行時にまったく言及されず、実際にはコードは追加のチェックなしでテスト、ブランチ、ロード、リターンのみですしたがって、これは(もう)問題ではなく、実際にJava 8の開発中に一時的な問題であったように見えます。

102
dhke

これは quite old commit 2013年5月3日、公式のJDK-8リリースのほぼ1年前に導入されました。当時、コンパイラは大いに開発中であったため、このような互換性の問題が発生する可能性がありました。おそらく、SpringチームはJDK-8ビルドをテストし、実際にはコンパイラーの問題であるにもかかわらず、問題の修正を試みただけだと思います。 JDK-8の公式リリースでは、これは無関係になりました。これで、このコードの三項演算子は期待どおりに機能します(コンパイルされた.classファイルのExecutableクラスへの参照は存在しません)。

現在、JDK-9でも同様のことが起こります。JDK-8でうまくコンパイルできるコードの一部は、JDK-9 javacで失敗します。そのような問題のほとんどはリリースまで修正されると思います。

30
Tagir Valeev

主な違いは、ifelseブロックがstatementであるのに対し、三項(Javaではconditional演算子としてよく知られている) です。

statementは、いくつかの制御パスで呼び出し元に対してreturnなどのことを実行できます。 expressionは、割り当てで使用できます。

int n = condition ? 3 : 2;

したがって、条件の後の3項の2つの式は、同じ型に強制変換可能である必要があります。これは、Javaで特に奇妙な効果を引き起こす可能性があります。特に自動ボックス化および自動参照キャストで-これは投稿コードのコメントが参照しているものです。あなたの場合の式の強制はJava.lang.reflect.Executableタイプ(最も特殊化されたタイプ)であり、古いバージョンのJavaには存在しません。

コードが文のような場合はifelseブロックを使用し、式のような場合は3項を使用する必要があります。

もちろん、ラムダ関数を使用する場合、ifelseブロックを式のように動作させることができます。

7
Bathsheba

三項式の戻り値の型は、Java 8。

キャストが書けなかった理由がわかりにくい。

6
user207421