36 votes

Opérateur Java conditionnel?: Type de résultat

Je suis un peu perplexe sur l'opérateur conditionnel. Considérons les deux lignes suivantes:

Float f1 = false? 1.0f: null;
Float f2 = false? 1.0f: false? 1.0f: null;

Pourquoi la f1 devient nulle et la deuxième déclaration, déclenche une exception NullPointerException?

Langspec-3.0 para 15.25 sais:

Sinon, la deuxième et la troisième opérandes sont de types S1 et S2 respectivement. Laissez-T1 être le type qui résulte de l'application de la boxe de conversion à S1, et de laisser T2 être le type qui résulte de l'application de la boxe de conversion à S2. Le type de l'expression conditionnelle est le résultat de l'application de capture de conversion (§5.1.10) à lub(T1, T2) (§15.12.2.7).

Donc, pour false?1.0f:null T1 est le Flotteur et T2 est le type null. Mais quel est le résultat de l' lub(T1,T2)? Ce paragraphe 15.12.2.7 est juste un peu trop ...

BTW, je suis en utilisant 1.6.0_18 sur Windows.

PS: je sais qu' Float f2 = false? (Float) 1.0f: false? (Float) 1.0f: null; ne jetez pas les entrées en phase nationale.

26voto

Bert F Points 27237

La différence est de type statique de l'expression au moment de la compilation:

Résumé

E1: `(false ? 1.0f : null)`
    - arg 2 '1.0f'           : type float,
    - arg 3 'null'           : type null 
    - therefore operator ?:  : type Float (see explanation below)
    - therefore autobox arg2
    - therefore autobox arg3

E2: `(false ? 1.0f : (false ? 1.0f : null))`
    - arg 2 '1.0f'                    : type float
    - arg 3 '(false ? 1.0f : null)'   : type Float (this expr is same as E1)
    - therefore, outer operator ?:    : type float (see explanation below)
    - therefore un-autobox arg3

Explication Détaillée:

Voici ce que je comprends de la lecture par le biais de la spécification et de travailler à rebours à partir du résultat que vous avez obtenu. Il s'agit du type de la troisième opérande de l'f2 intérieure conditionnel est de type null alors que le type de la troisième opérande de l'f2 extérieur conditionnelle est réputé pour être le Flotteur.

Remarque: il est important de rappeler que la détermination du type et de l'insertion de boxing/unboxing code est fait au moment de la compilation. D'exécution réel du boxing/unboxing code est fait au moment de l'exécution.

Float f1 = (false ? 1.0f : null);
Float f2 = (false ? 1.0f : (false ? 1.0f : null));

La f1 conditionnelle et la f2 intérieur condition: le (faux ? 1.0 f : null)

La f1 conditionnelle et la f2 intérieure conditionnelle sont identiques: (faux ? 1.0 f : null). L'opérande de types de la f1 conditionnelle et la f2 intérieure conditionnelle sont:

type of second operand = float
type of third operand = null type (§4.1)

La plupart des règles du §15.25 sont passés, et cette évaluation finale est en effet appliquée:

Sinon, la deuxième et la troisième opérandes sont de types S1 et S2 respectivement. Laissez-T1 être le type qui résulte de l'application de la boxe de conversion à S1, et laissez-T2 être le type qui résulte de l'application de la boxe de conversion à S2. Le type de l'expression conditionnelle est le résultat de l'application de capture de conversion (§5.1.10) à lub(T1, T2) (§15.12.2.7).

S1 = float
S2 = null type
T1 = Float
T2 = null type
type of the f1 and f2 inner conditional expressions = Float

Depuis la f1, l'assignation à un Flotteur variable de référence, le résultat de l'expression (null) est attribué.

F2 extérieur condition: le (faux ? 1.0 f : [f2 intérieure conditionnelle])

Pour le f2 extérieur conditionnelle, les types sont:

type of second operand = float
type of third operand = Float

Notez la différence dans l'opérande types par rapport à la f1/f2 intérieure des conditions qui font référence à la valeur null littérale directement (§4.1). En raison de cette différence d'avoir 2 numérique-type convertible, cette règle du §15.12.2.7 s'applique:

  • Si, au contraire, la deuxième et la troisième opérandes ont des types qui sont convertibles (§5.1.8) pour les types numériques, alors il y a plusieurs cas: ...

    • Sinon, binaire numérique de promotion (§5.6.2) est appliqué à l'opérande types, et le type de l'expression conditionnelle est le promu type de la deuxième et de la troisième opérande. Notez que le binaire numérique de promotion effectue unboxing de conversion (§5.1.8) et de la valeur de conversion (§5.1.13).

En raison de l'unboxing de la conversion effectuée sur le résultat de la f2 intérieure conditionnelle (null), un NullPointerException est soulevée.

2voto

objects Points 6137

Le suivant va jeter un NPE que vous essayez d'attribuer une valeur null à une primitive

    float f1 = false ? 1.0f: null;

Je crois que ce qui est provoquant la NPE dans la deuxième instruction. Parce que le premier ternaire renvoie un float pour de vrai, il tente de convertir le faux à un flotteur ainsi.

La première instruction ne sera pas convertir la valeur null comme le résultat requis est un Float

Ceci par exemple, ce ne serait pas jeter un NPE que sa n'a plus besoin de les convertir à la primitive

    Float f = false? new Float(1.0f): true ? null : 1.0f;

2voto

JRL Points 36674

Je pense que la réécriture du code rend l'explication plus claire:

     float f = 1.0f;

    Float null_Float  = false?        f  : null;       // float + null  -> OK
    Float null_Float2 = false? (Float)f  : null_Float; // Float + Float -> OK
    Float npe         = false?        f  : null_Float; // float + Float -> NPE
 

Ainsi, le NPE est lorsque nous essayons de faire quelque chose comme:

 Float npe = false? 1.0f : (Float)null;
 

2voto

kloffy Points 1516

Être ou ne pas être, telle est la question. :)

Edit: en Fait, en regardant de plus près, il semble que ce cas est en fait un mélange entre le Hameau (opérateur ternaire et enveloppé les types intégraux) et Elvis (auto-unboxing null) casse-têtes. En tout cas, je ne peux que vous recommander de regarder la vidéo, c'est très instructif et agréable.

1voto

Slava Imeshev Points 482

Il semble que la machine virtuelle Java tente de décompresser le deuxième null pour qu'il flotte au lieu de Float , d'où NullPointerException. Frappe moi-même une fois. Mon point de vue est que le second si le fait parce que la vraie partie du premier si évalue comme un float, pas un float.

Après avoir réfléchi, je pense que c'est une façon de Java de vous dire que vous faites quelque chose d'étrange. Il suffit de ne pas imbriquer des ifs ternaires et tout ira bien :-)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X