¿Operador condicional Java?: Tipo de resultado

Estoy un poco desconcertado sobre el operador condicional. Considere las siguientes dos líneas:

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

¿Por qué f1 se vuelve nulo y la segunda statement arroja una NullPointerException?

Langspec-3.0 para 15.25 sais:

De lo contrario, el segundo y tercer operandos son de tipos S1 y S2, respectivamente. Deje que T1 sea del tipo que resulta de aplicar la conversión de boxeo a S1, y deje que T2 sea del tipo que resulta de aplicar la conversión de boxeo a S2. El tipo de expresión condicional es el resultado de aplicar la conversión de captura (§5.1.10) a lub (T1, T2) (§15.12.2.7).

Entonces para false?1.0f:null T1 es Float y T2 es el tipo nulo. Pero, ¿cuál es el resultado de lub(T1,T2) ? Este párrafo 15.12.2.7 es demasiado …

Por cierto, estoy usando 1.6.0_18 en Windows.

PD: Sé que Float f2 = false? (Float) 1.0f: false? (Float) 1.0f: null; Float f2 = false? (Float) 1.0f: false? (Float) 1.0f: null; no tira NPE

La diferencia es el tipado estático de las expresiones en tiempo de comstackción:

Resumen

 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 

Explicación detallada:

Aquí está mi entendimiento de leer las especificaciones y trabajar hacia atrás desde el resultado que obtuviste. Se reduce al tipo del tercer operando del condicional interno f2 es el tipo nulo, mientras que el tipo del tercer operando del condicional externo f2 se considera flotante.

Nota: Es importante recordar que la determinación del tipo y la inserción del código de boxeo / desempaquetado se realiza en tiempo de comstackción. La ejecución real del código de boxeo / unboxing se realiza en tiempo de ejecución.

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

El condicional f1 y el condicional interno f2: (falso? 1.0f: nulo)

El condicional f1 y el condicional interno f2 son idénticos: (falso? 1.0f: nulo) . Los tipos de operandos en el condicional f1 y en el condicional interno f2 son:

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

La mayoría de las reglas en §15.25 se pasan y esta evaluación final se aplica de hecho:

De lo contrario, el segundo y tercer operandos son de tipos S1 y S2, respectivamente. Deje que T1 sea del tipo que resulta de aplicar la conversión de boxeo a S1, y deje que T2 sea del tipo que resulta de aplicar la conversión de boxeo a S2. El tipo de expresión condicional es el resultado de aplicar la conversión de captura ( §5.1.10 ) a 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 

Como para f1, la asignación es a una variable de referencia de Float, el resultado de la expresión (nulo) se asigna con éxito.

Para f2 condicional externo: (falso? 1.0f: [f2 condicional interno])

Para el condicional externo f2, los tipos son:

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

Tenga en cuenta la diferencia en los tipos de operandos en comparación con los condicionales internos f1 / f2 que hacen referencia directamente al literal nulo ( §4.1 ). Debido a esta diferencia de tener 2 tipos de convertibles numéricos, se aplica esta regla de §15.12.2.7 :

  • De lo contrario, si el segundo y tercer operandos tienen tipos que son convertibles ( §5.1.8 ) a tipos numéricos, entonces hay varios casos: …

    • De lo contrario, la promoción numérica binaria ( §5.6.2 ) se aplica a los tipos de operandos, y el tipo de expresión condicional es el tipo promovido del segundo y tercer operandos. Tenga en cuenta que la promoción numérica binaria realiza la conversión de unboxing ( §5.1.8 ) y la conversión de conjuntos de valores ( §5.1.13 ).

Debido a la conversión de unboxing realizada en el resultado del condicional interno f2 (null), se genera una NullPointerException.

Lo siguiente arrojará un NPE cuando intente asignar un nulo a un primitivo

  float f1 = false ? 1.0f: null; 

Eso creo que está causando el NPE en la segunda statement. Debido a que el primer ternario devuelve un flotador verdadero, también intenta convertir el falso en un flotador.

La primera statement no se convertirá en nula ya que el resultado requerido es un Float

Esto, por ejemplo, esto no arrojaría un NPE ya que ya no necesita convertirse a primitivo

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

Creo que reescribir el código aclara la explicación:

  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 

Por lo tanto, la NPE es cuando intentamos hacer algo como:

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

Ser o no ser, esa es la pregunta. 🙂

Editar: En realidad, mirando más de cerca parece que este caso es en realidad una mezcla entre el Hamlet (operador ternario y tipos integrales envueltos) y los rompecabezas Elvis (nulo auto-unboxing). En cualquier caso, solo puedo recomendar ver el video, es muy educativo y agradable.

Parece que la JVM intenta desempaquetar el segundo valor nulo para flotar en lugar de Float , por lo tanto, NullPointerException. Golpéalo yo mismo una vez. Mi opinión es que la segunda si lo hace porque la parte verdadera de la primera si se evalúa como flotante, no como Float.

Después de pensarlo dos veces, creo que esta es una forma de que Java te diga que estás haciendo algo extraño. Simplemente no anides ifs ternarios y estarás bien 🙂