次のスニペットの単純な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を発行しますコンパイル時エラー!どうして?
コンパイラはnull
をInteger
へのnull参照として解釈し、条件演算子にオートボクシング/アンボクシングルールを適用します( Java Language Specification、15.25 で説明) 、そして上に幸せに移動します。これにより、実行時にNullPointerException
が生成されます。これは試して確認できます。
Javaコンパイラはtrue ? null : 0
をInteger
式として。暗黙的にint
に変換でき、場合によってはNullPointerException
になります。
2番目の場合、式null
は特別なnull型see であるため、コードreturn null
は、タイプが一致しません。
実際、そのすべては Java言語仕様 で説明されています。
条件式のタイプは次のように決定されます。
- 2番目と3番目のオペランドが同じ型(null型の場合もある)である場合、それは条件式の型です。
したがって、あなたの(true ? null : 0)
はint型を取得し、整数に自動ボックス化されます。
これを確認するには、このようなものを試してください(true ? null : null)
そして、コンパイラエラーが表示されます。
if
ステートメントの場合、 expression に参加していないため、null
参照はInteger
参照として扱われません。強制的に解釈されます。したがって、エラーは type エラーであるため、コンパイル時に簡単にキャッチできます。
条件演算子については、Java Language Specification§15.25“ Conditional Operator ? :
”は、型変換がどのように適用されるかのルールでこれにうまく答えています。
- 2番目と3番目のオペランドが同じ型(null型の場合もある)である場合、それは条件式の型です。
null
はint
ではないため適用されません。
- 2番目と3番目のオペランドの一方がブール型で、もう一方のタイプがブール型の場合、条件式のタイプはブール型になります。
null
もint
もboolean
またはBoolean
でもないため、適用されません。
- 2番目と3番目のオペランドの一方がNULL型で、もう一方の型が参照型である場合、条件式の型はその参照型になります。
null
はnull型ですが、int
は参照型ではないため、適用されません。
- それ以外の場合、2番目と3番目のオペランドが数値型に変換可能な型(§5.1.8)である場合、いくつかのケースがあります:[…]
適用:null
は数値型に変換可能として扱われ、NullPointerException
。
最初に心に留めておくべきことは、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
をスローします(これは起こりません)が、後者はint
をnull
にできないので意味がありません...
実際、最初の場合は式を評価できます。コンパイラはInteger
として評価する必要があることを知っているためです。ただし、2番目の場合は戻り値の型(null
)決定できないため、コンパイルできません。 Integer
にキャストすると、コードがコンパイルされます。
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
}
これはどう:
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として認識しています。