web-dev-qa-db-ja.com

if文ではなく三項演算子で許可されたintとしてnullを返します

次のスニペットの単純なJavaコードを見てみましょう。

_public class Main {

    private int temp() {
        return true ? null : 0;
        // No compiler error - the compiler allows a return value of null
        // in a method signature that returns an int.
    }

    private int same() {
        if (true) {
            return null;
            // The same is not possible with if,
            // and causes a compile-time error - incompatible types.
        } else {
            return 0;
        }
    }

    public static void main(String[] args) {
        Main m = new Main();
        System.out.println(m.temp());
        System.out.println(m.same());
    }
}
_

この最も単純なJavaコード)では、関数の戻り値の型がintであっても、temp()メソッドはコンパイラエラーを発行しません。値nullを返します(ステートメント_return true ? null : 0;_を使用)コンパイルすると、明らかにランタイム例外NullPointerExceptionが発生します。

ただし、三項演算子をifステートメント(same()メソッドのように)で表す場合、同じことは間違っているように見えますdoesを発行しますコンパイル時エラー!どうして?

185
Lion

コンパイラはnullIntegerへのnull参照として解釈し、条件演算子にオートボクシング/アンボクシングルールを適用します( Java Language Specification、15.25 で説明) 、そして上に幸せに移動します。これにより、実行時にNullPointerExceptionが生成されます。これは試して確認できます。

115
Ted Hopp

Javaコンパイラはtrue ? null : 0Integer式として。暗黙的にintに変換でき、場合によってはNullPointerExceptionになります。

2番目の場合、式nullは特別なnull型see であるため、コードreturn nullは、タイプが一致しません。

39
Vlad

実際、そのすべては Java言語仕様 で説明されています。

条件式のタイプは次のように決定されます。

  • 2番目と3番目のオペランドが同じ型(null型の場合もある)である場合、それは条件式の型です。

したがって、あなたの(true ? null : 0)はint型を取得し、整数に自動ボックス化されます。

これを確認するには、このようなものを試してください(true ? null : null)そして、コンパイラエラーが表示されます。

32
nowaq

ifステートメントの場合、 expression に参加していないため、null参照はInteger参照として扱われません。強制的に解釈されます。したがって、エラーは type エラーであるため、コンパイル時に簡単にキャッチできます。

条件演算子については、Java Language Specification§15.25“ Conditional Operator ? :”は、型変換がどのように適用されるかのルールでこれにうまく答えています。

  • 2番目と3番目のオペランドが同じ型(null型の場合もある)である場合、それは条件式の型です。

    nullintではないため適用されません。

  • 2番目と3番目のオペランドの一方がブール型で、もう一方のタイプがブール型の場合、条件式のタイプはブール型になります。

    nullintbooleanまたはBooleanでもないため、適用されません。

  • 2番目と3番目のオペランドの一方がNULL型で、もう一方の型が参照型である場合、条件式の型はその参照型になります。

    nullはnull型ですが、intは参照型ではないため、適用されません。

  • それ以外の場合、2番目と3番目のオペランドが数値型に変換可能な型(§5.1.8)である場合、いくつかのケースがあります:[…]

    適用:nullは数値型に変換可能として扱われ、NullPointerException
25
Jon Purdy

最初に心に留めておくべきことは、Java三項演算子には「タイプ」があり、これはコンパイラーが2番目または Java Language Specification 15.26 に示されているように、3項演算子のタイプはさまざまな方法で決定されます。

上記の質問では、最後のケースを考慮する必要があります。

それ以外の場合、第2および第3オペランドのタイプは、それぞれS1およびS2です。 T1をボクシング変換をS1に適用した結果の型とし、T2をボクシング変換を適用した結果の型とします- S2。条件式のタイプは、キャプチャ変換(5.1.10)をlub(T1、T2)(§15.12.2.7)に適用した結果です。

これは、 キャプチャ変換の適用(§5.1.10) 、そして何よりもlubを見ると、最も複雑なケースです。 (T1、T2)

平易な英語で、極端に単純化した後、2番目と3番目のパラメーターの「最小共通スーパークラス」(はい、LCMを考えてください)を計算するプロセスを説明できます。これにより、三項演算子「type」が得られます。繰り返しますが、先ほど述べたのは極端な単純化です(複数の共通インターフェースを実装するクラスを検討してください)。

たとえば、次を試してみると:

long millis = System.currentTimeMillis();
return(true ? new Java.sql.Timestamp(millis) : new Java.sql.Time(millis));

条件式の結果の型は、Timestamp/Timeペアの「最小共通スーパークラス」であるため、Java.util.Dateであることがわかります。

nullは任意のものに自動ボックス化できるため、「最小共通スーパークラス」はIntegerクラスであり、これは上記の条件式(三項演算子)の戻り値の型になります。戻り値はInteger型のNULLポインターになり、それが三項演算子によって返されます。

実行時に、Java仮想マシンがIntegerをアンボックスすると、NullPointerExceptionがスローされます。これは、JVMが関数null.intValue()を呼び出そうとするためです。ここでnullはオートボクシングの結果です。

私の意見では(そして、私の意見はJava Language Specificationにないので、多くの人が間違っていると思うでしょう)、コンパイラはあなたの質問の表現を評価するのに悪い仕事をします。 true ? param1 : param2最初のパラメーター-null-が返され、コンパイラーエラーを生成することをコンパイラーはすぐに判断する必要があります。これは、while(true){} etc...を記述し、コンパイラーがループとフラグの下のコードについて文句を言うときと似ていますUnreachable Statementsを使用します。

あなたの2番目のケースは非常に簡単で、この答えはすでに長すぎます...;)

修正:

別の分析の後、私はnull値を任意にボックス化/自動ボックス化できると言うのは間違っていたと信じています。クラスIntegerについて言えば、明示的なボクシングはnew Integer(...)コンストラクターまたは多分Integer.valueOf(int i);を呼び出すことにあります(このバージョンはどこかにあります)。前者はNumberFormatExceptionをスローします(これは起こりません)が、後者はintnullにできないので意味がありません...

11
Gevorg

実際、最初の場合は式を評価できます。コンパイラはIntegerとして評価する必要があることを知っているためです。ただし、2番目の場合は戻り値の型(null)決定できないため、コンパイルできません。 Integerにキャストすると、コードがコンパイルされます。

4
GeT
private int temp() {

    if (true) {
        Integer x = null;
        return x;// since that is fine because of unboxing then the returned value could be null
        //in other words I can say x could be null or new Integer(intValue) or a intValue
    }

    return (true ? null : 0);  //this will be prefectly legal null would be refrence to Integer. The concept is one the returned
    //value can be Integer 
    // then null is accepted to be a variable (-refrence variable-) of Integer
}
2
Youans

これはどう:

public class ConditionalExpressionType {

    public static void main(String[] args) {

        String s = "";
        s += (true ? 1 : "") instanceof Integer;
        System.out.println(s);

        String t = "";
        t += (!true ? 1 : "") instanceof String;
        System.out.println(t);

    }

}

出力はtrue、trueです。

Eclipseは、条件付き式の1をオートボックスとして色分けします。

私の推測では、コンパイラは式の戻り値の型をObjectとして認識しています。

0
Jon