java nullpointerexception conditional-operator autoboxing

Operador ternario complicado en Java-autoboxing



nullpointerexception conditional-operator (8)

Veamos el código simple de Java en el siguiente fragmento:

public class Main { private int temp() { return true ? null : 0; // No compiler error - the compiler allows a return value of null // in a method signature that returns an int. } private int same() { if (true) { return null; // The same is not possible with if, // and causes a compile-time error - incompatible types. } else { return 0; } } public static void main(String[] args) { Main m = new Main(); System.out.println(m.temp()); System.out.println(m.same()); } }

En este código Java simple, el método temp() no emite ningún error de compilación aunque el tipo de retorno de la función es int , y estamos tratando de devolver el valor null (a través de la declaración return true ? null : 0; ). Cuando se compila, esto obviamente causa la excepción de tiempo de ejecución NullPointerException .

Sin embargo, parece que lo mismo es incorrecto si representamos al operador ternario con una instrucción if (como en el same() método same() ), que emite un error en tiempo de compilación. ¿Por qué?


Creo que el compilador de Java interpreta true ? null : 0 true ? null : 0 como una expresión de Integer , que se puede convertir implícitamente a int , posiblemente dando NullPointerException .

Para el segundo caso, la expresión null es del tipo nulo especial see , por lo que el código return null hace que el tipo no coincida.


El compilador interpreta null como una referencia nula a un Integer , aplica las reglas de autoboxing / unboxing para el operador condicional (como se describe en la Especificación del lenguaje Java, 15.25 ), y se mueve felizmente. Esto generará una NullPointerException en tiempo de ejecución, que puede confirmar intentándolo.


En el caso de la instrucción if , la referencia null no se trata como una referencia de Integer porque no está participando en una expresión que obliga a interpretarla como tal. Por lo tanto, el error puede detectarse fácilmente en tiempo de compilación porque es más claramente un error de tipo .

En cuanto al operador condicional, la especificación de lenguaje Java §15.25 " ? : Operador condicional ? : ? : "Responde esto muy bien en las reglas sobre cómo se aplica la conversión de tipo:

  • Si el segundo y tercer operandos tienen el mismo tipo (que puede ser el tipo nulo), ese es el tipo de expresión condicional.

    No se aplica porque null no es int .
  • Si uno de los operandos segundo y tercero es de tipo booleano y el tipo de otro es de tipo booleano, entonces el tipo de expresión condicional es booleano.

    No se aplica porque ni null ni int son boolean o Boolean .
  • Si uno de los operandos segundo y tercero es del tipo nulo y el tipo del otro es un tipo de referencia, entonces el tipo de expresión condicional es ese tipo de referencia.

    No se aplica porque null es del tipo nulo, pero int no es un tipo de referencia.
  • 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: [...]

    Se aplica: null se trata como convertible en un tipo numérico, y se define en §5.1.8 "Conversión de unboxing" para arrojar una NullPointerException .

En realidad, en el primer caso la expresión se puede evaluar, como sabe el compilador, que debe evaluarse como un Integer , sin embargo, en el segundo caso no se puede determinar el tipo del valor de retorno ( null ), por lo que no se puede compilado. Si lo Integer a Integer , el código se compilará.


En realidad, todo se explica en la see .

El tipo de expresión condicional se determina de la siguiente manera:

  • Si el segundo y tercer operandos tienen el mismo tipo (que puede ser el tipo nulo), ese es el tipo de expresión condicional.

Por lo tanto, el "nulo" en su (true ? null : 0) obtiene un tipo int y luego se autoenlace a Integer.

Intente algo como esto para verificar esto (true ? null : null) y obtendrá el error del compilador.


Lo primero a tener en cuenta es que los operadores ternarios de Java tienen un "tipo", y que esto es lo que el compilador determinará y considerará sin importar cuáles sean los tipos real / real del segundo o tercer parámetro. Dependiendo de varios factores, el tipo de operador ternario se determina de diferentes maneras, como se ilustra en la Especificación del lenguaje Java 15.26.

En la pregunta anterior, deberíamos considerar el último caso:

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

Este es, con mucho, el caso más complejo una vez que eche un vistazo a la aplicación de conversión de captura (§5.1.10) y, sobre todo, en lub (T1, T2) .

En inglés simple y después de una simplificación extrema podemos describir el proceso como el cálculo de la "Superclase común mínima" (sí, piense en el LCM) del segundo y tercer parámetro. Esto nos dará el operador ternario "tipo". De nuevo, lo que acabo de decir es una simplificación extrema (consideremos las clases que implementan múltiples interfaces comunes).

Por ejemplo, si intenta lo siguiente:

long millis = System.currentTimeMillis(); return(true ? new java.sql.Timestamp(millis) : new java.sql.Time(millis));

Notarás que el tipo resultante de la expresión condicional es java.util.Date ya que es la "Superclase común más baja" para el par Timestamp / Time .

Como null se puede autocapturar a cualquier cosa, la "Superclase común mínima" es la clase Integer y este será el tipo de retorno de la expresión condicional (operador ternario) anterior. El valor de retorno será entonces un puntero nulo de tipo Integer y eso es lo que devolverá el operador ternario.

En tiempo de ejecución, cuando la Máquina Virtual de Java descompacta el Integer genera una ExcepciónNivelPointer. Esto sucede porque la JVM intenta invocar la función null.intValue() , donde null es el resultado del autoboxing.

En mi opinión (y como mi opinión no está en la Especificación del lenguaje Java, muchas personas lo encontrarán mal de todos modos) el compilador hace un trabajo pobre al evaluar la expresión en su pregunta. Dado que escribiste true ? param1 : param2 true ? param1 : param2 el compilador debe determinar de inmediato que se devolverá el primer parámetro - null - y generará un error de compilación. Esto es algo similar a cuando escribe while(true){} etc... y el compilador se queja del código debajo del bucle y lo marca con Unreachable Statements .

Su segundo caso es bastante sencillo y esta respuesta ya es demasiado larga ...;)

CORRECCIÓN:

Después de otro análisis, creo que me equivoqué al decir que un valor null puede encasillarse / autoboxearse a cualquier cosa. Hablando sobre la clase Integer, el boxeo explícito consiste en invocar el new Integer(...) constructor new Integer(...) o quizás el Integer.valueOf(int i); (Encontré esta versión en algún lado). El primero lanzaría una NumberFormatException (y esto no sucede) mientras que el segundo simplemente no tendría sentido ya que un int no puede ser null ...


Qué tal esto:

public class ConditionalExpressionType { public static void main(String[] args) { String s = ""; s += (true ? 1 : "") instanceof Integer; System.out.println(s); String t = ""; t += (!true ? 1 : "") instanceof String; System.out.println(t); } }

La salida es verdadera, verdadera.

Eclipse color codifica el 1 en la expresión condicional como autoboxed.

Mi suposición es que el compilador está viendo el tipo de retorno de la expresión como Object.


private int temp() { if (true) { Integer x = null; return x;// since that is fine because of auto-boxing then the returned value could be null //in other words I can say x could be null or new Integer(intValue) or a intValue } return (true ? null : 0); //this will be prefectly legal null would be refrence to Integer. The concept is one the returned //value can be Integer // then null is accepted to be a variable (-refrence variable-) of Integer }