2012-10-08 12 views
8

I seguenti programma stampa, rispettivamente, 'false' e 'vero':indesiderati autoboxing magia sui Numeri

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

Quindi non sarà una lunga, ma una doppia. Tuttavia, funziona come previsto sulle classi normali: Avere

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

questo stampa 'vero':

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

il che significa che non funziona lo stesso come l'esempio di cui sopra.

Sono sicuro che c'è qualcosa di correlato al boxing automatico, ma è davvero il modo in cui dovrebbe funzionare? Perché usa il pugilato, quando la classe Numero è una superclasse sia lunga che doppia, in modo che l'espressione possa essere valutata su Numero?

È davvero un problema, perché quando si stampa il n, viene stampato come un doppio valore. (So ​​che è facile soluzione, ma mi faceva impazzire)

+0

Il ':' prende il tipo dell'ultima espressione. Se lo hai reso 'null' sarebbe un' Long': P. –

+0

No, non prende il tipo di ultima espressione, basta guardare il secondo esempio. Ovviamente il codice è solo un esempio, ma immagina che ci sia qualcosa di vero di informazione booleana dove ora il 'vero' sta in trenario. – poroszd

+2

Perché i voti ravvicinati? Mi sembra una domanda dannatamente buona. –

risposta

7

Facciamo fuori il libro dell'avvocato lingua qui: JLS §15.25

Il tipo di un'espressione condizionale è determinato come segue:

  • Se il secondo e il terzo operando hanno lo stesso tipo (che può essere il tipo null), questo è il tipo di espressione condizionale.

lungo e doppio non sono dello stesso tipo - non si applica.

  • Se una delle seconde e terze operandi è di tipo primitivo T, e il tipo di l'altro è il risultato dell'applicazione di conversione boxe (§5.1.7) a T, allora il tipo di espressione condizionale è T.

Né valore è primitivo - non si applica.

  • Se una delle seconde e terze operandi è di tipo nullo e il tipo di l'altro è un tipo di riferimento, allora il tipo di espressione condizionale è che tipo di riferimento.

Né valore è nullo - non si applica.

  • Altrimenti, se il secondo e terzo operandi hanno tipi convertibili (§5.1.8) per tipi numerici, allora ci sono diversi casi:
    • [... casi particolari per byte/short/char e i loro equivalenti in scatola ...]
    • Altrimenti, la promozione numerica binaria (§ 5.6.2) viene applicata ai tipi di operandi e il tipo di espressione condizionale è il tipo promosso del secondo e del terzo operando.

Questa regola diffusa qui, il che significa che il tipo risultato dell'operatore condizionale è come se entrambi i valori sono unboxed. Supponiamo che il motivo sottostante sia che altrimenti Number n = bool ? 1 : 2.0 e Number n = bool ? new Long(1) : new Double(2.0) hanno valori diversi. Questo comportamento sarebbe anche inaspettato e, peggio, incoerente.

2

'semplicemente osserva il codice di byte e si vedrà (semplicemente modificato il tuo esempio)

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

Codice Byte

_new 'java/lang/Long'

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 

Il punto principale è l2d effettua i passi successivi

Espelle un intero lungo fuori dalla pila, lo lancia in una doppia precisione numero in virgola mobile e spinge il doppio indietro nello stack. Si noti che ciò può causare una perdita di precisione (il significato in un doppio è di 54 bit, rispetto a 64 bit per il lungo) sebbene non la perdita di (poiché l'intervallo di un doppio è maggiore dell'intervallo di un intervallo). L'arrotondamento viene eseguito utilizzando la modalità IEEE 754 round-to-closest.

E dopo tutto questo è buono, in modo da avere doppio grado, ma con lungo Valore! Se si guarda in modalità debug si vedrà che il nostro numero è il doppio ma il valore da Long, Descrive sopra in bytecode

possiamo vederlo in bytecode

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

Dolce, è bello sapere (anche se questo risponde al come, non al perché) . – poroszd