2012-10-08 19 views
8

Poniższe program wypisuje odpowiednio „false” i „true”:Niechciane autoboxing magia na liczbach

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

Więc to nie będzie długi, ale dwukrotnie. Jednak to działa zgodnie z przeznaczeniem na normalnych zajęć: Mając

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

to wypisze „true”:

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

co oznacza, że ​​nie działa tak samo jak w powyższym przykładzie.

Jestem pewien, że jest coś związanego z autoboxingiem, ale czy tak naprawdę to powinno działać? Dlaczego używa boksowania, gdy klasa Liczba jest nadklasą zarówno długich, jak i podwójnych, tak aby wyrażenie mogło być ocenione jako liczba?

To naprawdę ból, ponieważ podczas drukowania n, drukuje jako podwójna wartość. (Wiem, że to jest łatwe do obejścia, ale zawiózł mnie do szału)

+0

Opcja ':' zajmuje typ ostatniego wyrażenia. Jeśli zrobiłeś 'null' byłoby to' Long': P. –

+0

Nie, nie przyjmuje typu ostatniego wyrażenia, wystarczy spojrzeć na drugi przykład. Oczywiście kod jest tylko przykładem, ale wyobraź sobie, że istnieje coś prawdziwego, boolowskiego, gdzie teraz "prawdziwy" stoi w trendzie. – poroszd

+2

Dlaczego bliskie głosy? To wygląda mi na cholernie dobre pytanie. –

Odpowiedz

7

Weźmy na książki prawnika językowej tutaj: JLS §15.25

Rodzaj wyrażenia warunkowego jest określona następująco:

  • Jeśli drugi i trzeci operand mają ten sam typ (który może być typu pustego), to jest to typ wyrażenia warunkowego.

Długie i dwukrotnie nie są tego samego typu - nie ma zastosowania.

  • Jeżeli jeden z drugim i trzecim argumentów jest pierwotny typu T, a typ a druga jest wynikiem zastosowania konwersji boks (§5.1.7) do T, to typ warunkowa ekspresja to T.

Żadna z wartości nie jest prymitywna - nie dotyczy.

  • Jeżeli jeden z drugim i trzecim operandy zerowej typu i rodzaju drugi jest typu urządzenia, wówczas typ ekspresji warunkowej jest oznaczenie typu.

Żadna z wartości nie jest pusta - nie dotyczy.

  • W przeciwnym razie, jeśli drugi i trzeci argumenty mają typy, które są wymienialne (§5.1.8) do typów liczbowych, to istnieje kilka przypadków:
    • [... szczególne przypadki dla bajt/krótkie/char i ich zapakowane odpowiedniki ...]
    • W przeciwnym razie binarna promocja numeryczna (§ 5.6.2) jest stosowana do typów argumentów, a typ wyrażenia warunkowego jest typem promowanym drugiego i trzeciego argumentu operacji.

Zasada ta ma tu zastosowania, co oznacza, że ​​typ wynik operatora warunkowego jest jakby obie wartości były rozpakowanych. Przypuszczam, że powodem było to, że w innym przypadku i Number n = bool ? new Long(1) : new Double(2.0) mają różne wartości. Takie zachowanie byłoby również nieoczekiwane i - co gorsza - niekonsekwentne.

2

Prosto Spójrz na kod bajtowy i widać (po prostu zmodyfikowany przykład)

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

Kod Byte

_new 'java/lang/Długi'

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 

Główny punkt to l2d powoduje kolejne kroki:

Powoduje wyrzucenie długiej liczby całkowitej ze stosu, rzutuje ją na podwójną precyzję numer zmiennoprzecinkowy i przesuwa podwójne z powrotem na stos. Należy zauważyć, że może to spowodować utratę precyzji (wartość istotna w dublecie podwójna wynosi 54 bity, w porównaniu do 64 bitów dla długości), ale nie ma straty wielkości (ponieważ zakres podwójny jest większy niż zakres ). Zaokrąglanie odbywa się w trybie round-to-nearest IEEE 754.

A przecież to wszystko jest dobre, więc trzeba będzie podwójne instancji, ale z Długie Wartość! Jeśli spojrzeć w trybie debugowania widać, że nasza liczba jest dwukrotnie ale wartość z długo, to opisano powyżej w kodu bajtowego

Widzimy go w kodu bajtowego

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

Słodko, dobrze wiedzieć (nawet jeśli to odpowiada, a nie dlaczego) . – poroszd