2012-10-08 25 views
8

次のプログラムを印刷それぞれ「偽の」と「真」:不要なオートボクシングの魔法

Number n = true ? new Long(1) : new Double(2.0); 
System.out.println(n instanceof Long); 
System.out.println(n instanceof Double); 

だからそれは長いが、ダブルではありません。しかし、それは通常のクラスに意図したとおりに動作します: が、これは「真」に印刷されます

class B {} 
class D1 extends B {} 
class D2 extends B {} 

を持つ:

B b = true ? new D1() : new D2(); 
System.out.println(b instanceof D1); 

を、それは上記の例のように同じように動作していないことを意味しています。

私はautoboxingに関連するものがあると確信していますが、本当にそれはうまくいくはずですか? NumberクラスがLongとDoubleの両方のスーパークラスであるため、式をNumberに評価できるようにするために、なぜボクシングを使用するのですか?

nを印刷すると、二重の値として印刷されるので、本当に苦しいです。 (私は回避しやすいですけど、狂気私を運転した)

+0

は '?'型を取ります最後の式のもしそれを 'null'にすると' Long':Pになります。 –

+0

いいえ、最後の式の型を取りません。ちょうど2番目の例を見てください。もちろんコードは単なる例ですが、本当のブール情報が存在すると想像してください。ここで、本当のものはどこにあるのでしょうか。 – poroszd

+2

なぜ近い票?これは私にとってうれしい質問のようです。 –

答えて

7

のは、ここでは、言語の弁護士の著書を出してみましょう:JLS §15.25

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

  • 第2オペランドと第3オペランドの型が同じであれば(null型かもしれません)、それが条件式の型です。

ロングとダブルは同じタイプではありません - 適用されません。

  • 第二及び第三のオペランドの一方はプリミティブ型Tであり、および他のタイプの (§5.1.7)Tに、次いで型ボクシング変換を適用した結果である場合条件式はTです。

どちらの値もプリミティブではありません - 適用されません。

  • 第二及び第三のオペランドの一つがヌル型であり、他のタイプが参照型である場合、 条件式のタイプは、参照型です。

どちらの値もnullではありません - 適用されません。

  • 2番目及び3番目のオペランドが数値型へ(§5.1.8)変換可能なタイプを持っている場合はそれ以外の場合は、その後、いくつかの例があります。
    • バイト/ショート/のための[...特殊なケースcharとboxed同等物...]
    • それ以外の場合は、オペランドタイプに2進数値昇格(§5.6.2)が適用され、条件式のタイプは第2および第3オペランドの昇格タイプになります。

このルールは、両方の値がアンボックス化であるかのように条件演算子の結果型であることを意味し、ここで適用されます。その背後にある理由は、Number n = bool ? 1 : 2.0Number n = bool ? new Long(1) : new Double(2.0)の値が異なることです。この挙動は予期せぬことであり、さらに悪い - 一貫性がない。

2

単に ルックバイトコードへの、あなたは

Number n = true ? new Long(166666) : new Double(24444.0); 
System.out.println(Boolean.toString(n instanceof Long)); 
System.out.println(Boolean.toString(n instanceof Double)); 

バイトコード

_new 'のjava/LANG /ロング'

dup 
ldc 166666 
invokespecial 'java/lang/Long.<init>','(J)V' 
invokevirtual 'java/lang/Long.longValue','()J' 
l2d 
invokestatic 'java/lang/Double.valueOf','(D)Ljava/lang/Double;' 
astore 1 

(単に変更あなたの例)が表示されます主なポイントはl2d次のステップを行う

スタックから長整数をポップし、倍精度 浮動小数点数にキャストし、倍精度浮動小数点数をスタックにプッシュします。 これは、精度が失われる可能性があることに注意してください( の有効数字は、64ビットと比較して54ビットですが、損失はありません) (doubleの範囲は の長さより長いため長い)。丸めは、IEEE 754ラウンド・ツー・ラウンド・モードを使用して行われます。

そして、この後、すべては良いですが、あなたはダブルインスタンスを持っていますが、long値となります!あなたがデバッグモードで見ると、あなたが私たちの数はダブルが、ロングからの値であることがわかります、それは

我々はバイトコードでそれを見ることができ、バイトコードに上記の説明

getstatic 'java/lang/System.out','Ljava/io/PrintStream;' 
aload 1 
_instanceof 'java/lang/Long' 
invokestatic 'java/lang/Boolean.toString','(Z)Ljava/lang/String;' 
invokevirtual 'java/io/PrintStream.println','(Ljava/lang/String;)V' 
getstatic 'java/lang/System.out','Ljava/io/PrintStream;' 
aload 1 
_instanceof 'java/lang/Double' 
invokestatic 'java/lang/Boolean.toString','(Z)Ljava/lang/String;' 
invokevirtual 'java/io/PrintStream.println','(Ljava/lang/String;)V' 
return 
+0

甘い、それは知っているのは良いです(たとえこれが答えたとしても、理由はありません) 。 – poroszd

関連する問題