Veamos el código Java simple 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 más simple, el temp()
método no emite ningún error del compilador a pesar de que 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 está mal si representamos el operador ternario con una if
declaración (como en el same()
método), que hace emitir un error en tiempo de compilación! ¿Por qué?
int foo = (true ? null : 0)
ynew Integer(null)
ambos compilan bien, el segundo es la forma explícita de autoboxing.null
aInteger
... Eso mismo aspecto que "adivinar" a mí o "hacer las cosas de trabajo" ...Integer foo() { return "1"; }
no se compilará).Respuestas:
El compilador interpreta
null
como una referencia nula a unInteger
, aplica las reglas de autoboxing / unboxing para el operador condicional (como se describe en la Especificación del lenguaje Java, 15.25 ), y continúa felizmente. Esto generará unNullPointerException
tiempo de ejecución, que puede confirmar probándolo.fuente
capture conversion
ylub(T1,T2)
) ?? Además, ¿es realmente posible aplicar el boxeo a un valor nulo? ¿No sería esto como "adivinar"?lub(T1,T2)
es el tipo de referencia más específico en común en la jerarquía de tipos de T1 y T2. (Ambos comparten al menos un Objeto, por lo que siempre hay un tipo de referencia más específico).null
no está encajonado en un entero, se interpreta como una referencia a un entero (una referencia nula, pero eso no es un problema). No se construye ningún objeto entero a partir del nulo, por lo que no hay razón para una NumberFormatException.null
(que no es un tipo numérico primitivo), la cláusula aplicable es "Si p es un valor de cualquier otro tipo, la conversión de boxeo es equivalente a una conversión de identidad ". Entonces, la conversión del boxeonull
aInteger
rendimientosnull
, sin invocar a ningúnInteger
constructor.Creo que el compilador de Java interpreta
true ? null : 0
como unaInteger
expresión, que se puede convertir implícitamenteint
, posiblemente dandoNullPointerException
.Para el segundo caso, la expresión
null
es del tipo nulo especial ver , por lo que el códigoreturn null
hace que el tipo no coincida.fuente
true ? null : 0
comoInteger
? Por autoboxing0
primero ??En realidad, todo se explica en la Especificación del lenguaje Java .
Por lo tanto, el "nulo" en su
(true ? null : 0)
obtiene un tipo int y luego se autoboxing a Integer.Pruebe algo como esto para verificar esto
(true ? null : null)
y obtendrá el error del compilador.fuente
int
valor de la función, lo que provoca un NPE.null
aInteger
lanew Integer(null);
"Let T1 sea del tipo que resulta de aplicar la conversión de boxeo a S1 ..." que se obtendría unaNumberFormatException
y esto no es el caso ...En el caso de la
if
declaración, lanull
referencia no se trata como unaInteger
referencia porque no participa 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 del lenguaje Java §15.25 "Operador condicional
? :
" responde esto muy bien en las reglas sobre cómo se aplica la conversión de tipo:fuente
0
está en caja automáticaInteger
, el compilador está ejecutando el último caso de las "reglas de operador ternario" como se describe en la Especificación del lenguaje Java. Si eso es cierto, es difícil para mí creer que saltaría al caso 3 de las mismas reglas que tienen un tipo nulo y un tipo de referencia que hace que el valor de retorno del operador ternario sea el tipo de referencia (Entero). .Integer
? Eso es exactamente lo que está sucediendo; el NPE se genera al tratar de desempaquetar el valor de la expresión para devolver un valorint
de la función. Cambie la función para devolver unInteger
y volveránull
sin problema.null
se encuentre en esta categoría . Además, pasaríamos al paso "De lo contrario, se aplica la promoción numérica binaria (§5.6.2) ... Tenga en cuenta que la promoción numérica binaria realiza la conversión de desempaquetado (§5.1.8) ..." para determinar el tipo de retorno. Pero la conversión de unboxing generaría un NPE y esto ocurre solo en tiempo de ejecución y no mientras se intenta determinar el tipo de operador ternario. Todavía estoy confundido ..null
se trata como si tuviera tipoint
, pero en realidad es equivalente athrow new NullPointerException()
, eso es todo.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 reales / reales 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:
Este es, con mucho, el caso más complejo una vez que echa un vistazo a la aplicación de la 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 menos común" (sí, piense en el MCM) del segundo y tercer parámetro. Esto nos dará el operador "tipo" ternario. Nuevamente, lo que acabo de decir es una simplificación extrema (considere las clases que implementan múltiples interfaces comunes).
Por ejemplo, si intenta lo siguiente:
Notarás que el tipo resultante de la expresión condicional se debe a
java.util.Date
que es la "Superclase menos común" para elTimestamp
/Time
par.Dado que
null
se puede autoboxear a cualquier cosa, la "Superclase menos común" es laInteger
clase 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 tipoInteger
y eso es lo que devolverá el operador ternario.En tiempo de ejecución, cuando la máquina virtual de Java unboxes la
Integer
unaNullPointerException
se lanza. Esto sucede porque la JVM intenta invocar la funciónnull.intValue()
, dondenull
es el resultado del autoboxing.En mi opinión (y dado que 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 mal trabajo al evaluar la expresión en su pregunta. Dado que usted escribió,
true ? param1 : param2
el compilador debe determinar de inmediato quenull
se devolverá el primer parámetro - y generará un error de compilación. Esto es algo similar a cuando escribewhile(true){} etc...
y el compilador se queja del código debajo del bucle y lo marcaUnreachable 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
null
valor se puede encuadrar / encuadrar automáticamente en cualquier cosa. Hablando de la clase Integer, el boxeo explícito consiste en invocar alnew Integer(...)
constructor o tal vez elInteger.valueOf(int i);
(encontré esta versión en alguna parte). El primero arrojaría unNumberFormatException
(y esto no sucede) mientras que el segundo simplemente no tendría sentido ya que unint
no puede sernull
...fuente
null
código original en OP no está encuadrado. La forma en que funciona es: el compilador supone quenull
es una referencia a un número entero. Usando las reglas para los tipos de expresiones ternarias, decide que la expresión completa es una expresión entera. Luego genera código para autobox el1
(en caso de que la condición evalúefalse
). Durante la ejecución, la condición se evalúa como, portrue
lo que la expresión se evalúa comonull
. Al intentar devolver unint
de la función, elnull
está sin caja. Eso luego arroja un NPE. (El compilador podría optimizar la mayor parte de esto.)En realidad, en el primer caso, la expresión se puede evaluar, ya que el compilador sabe que debe evaluarse como un
Integer
; sin embargo, en el segundo caso, el tipo del valor de retorno (null
) no se puede determinar, por lo que no se puede compilar. Si lo envíaInteger
, el código se compilará.fuente
fuente
Qué tal esto:
La salida es verdadera, verdadera.
El color del eclipse codifica el 1 en la expresión condicional como autoboxed.
Supongo que el compilador está viendo el tipo de retorno de la expresión como Object.
fuente