Fue muy confuso para mí observar esta situación:
Integer i = null;
String str = null;
if (i == null) { //Nothing happens
...
}
if (str == null) { //Nothing happens
}
if (i == 0) { //NullPointerException
...
}
if (str == "0") { //Nothing happens
...
}
Entonces, como creo que la operación de boxeo se ejecuta primero (es decir, Java intenta extraer el valor int null
) y la operación de comparación tiene una prioridad más baja, por eso se lanza la excepción.
La pregunta es: ¿por qué se implementa de esta manera en Java? ¿Por qué el boxeo tiene mayor prioridad que comparar referencias? ¿O por qué no implementaron la verificación null
antes del boxeo?
Por el momento, parece inconsistente cuando NullPointerException
se lanza con primitivas envueltas y no se lanza con tipos de objetos verdaderos .
java
nullpointerexception
boxing
romano
fuente
fuente
Respuestas:
La respuesta corta
El punto clave es este:
==
entre dos tipos de referencia es siempre una comparación de referenciaInteger
yString
, querría usarequals
en su lugar==
entre un tipo de referencia y un tipo primitivo numérico es siempre una comparación numéricanull
siempre arrojaNullPointerException
String
, de hecho NO es un tipo primitivoLas declaraciones anteriores son válidas para cualquier código Java válido dado . Con este entendimiento, no hay inconsistencia alguna en el fragmento que presentó.
La respuesta larga
Aquí están las secciones relevantes de JLS:
Esto explica lo siguiente:
Integer i = null; String str = null; if (i == null) { // Nothing happens } if (str == null) { // Nothing happens } if (str == "0") { // Nothing happens }
Ambos operandos son tipos de referencia, y es por eso que la
==
comparación de igualdad de referencia es.Esto también explica lo siguiente:
System.out.println(new Integer(0) == new Integer(0)); // "false" System.out.println("X" == "x".toUpperCase()); // "false"
Para
==
ser igualdad numérica, al menos uno de los operandos debe ser de tipo numérico :Esto explica:
Integer i = null; if (i == 0) { //NullPointerException }
Aquí hay un extracto de Effective Java 2nd Edition, Item 49: Prefiere primitivas a primitivas en caja :
Hay lugares en los que no tiene más remedio que utilizar primitivas en caja, por ejemplo, genéricos, pero de lo contrario debería considerar seriamente si se justifica la decisión de utilizar primitivas en caja.
Referencias
Integer
a otroint
"r
es asínull
, la conversión de unboxing arroja unNullPointerException
"==
y!=
==
y!=
Preguntas relacionadas
Integers
en Java, ¿ocurre el desempaquetado automático?==
pero noequals()
?Preguntas relacionadas
int num = Integer.getInteger("123")
lanzaNullPointerException
? (!!!)String.equals
versus==
fuente
someRef == 0
siempre es una comparación numérica, es una elección muy acertada, ya que comparar las referencias de dos primitivas en caja es casi siempre un error del programador. Sería inútil establecer por defecto comparaciones de referencia en este caso.(myInteger == 0)
con en(myInteger != null && myInteger == 0)
lugar de depender del desarrollador para escribir este código de verificación de nulos estándar? En mi opinión, debería poder verificarif (myBoolean)
y eso debería evaluartrue
si y solo si el valor subyacente es específicamentetrue
, no debería tener que verificar nulo primero.Su ejemplo de NPE es equivalente a este código, gracias al autoboxing :
if ( i.intValue( ) == 0 )
Por tanto, NPE si
i
esnull
.fuente
if (i == 0) { //NullPointerException ... }
i es un entero y el 0 es un int, por lo que lo que realmente se hace es algo como esto
i.intValue() == 0
Y esto causa el nullPointer porque la i es nula. Para String no tenemos esta operación, por eso no hay una excepción allí.
fuente
Los creadores de Java podrían haber definido al
==
operador para que actúe directamente sobre operandos de diferentes tipos, en cuyo caso, dadaInteger I; int i;
la comparación,I==i;
podrían plantearse la pregunta "¿TieneI
una referencia a unInteger
valor de quién esi
?", Una pregunta que podría responderse sin dificultad. incluso cuandoI
es nulo. Desafortunadamente, Java no verifica directamente si los operandos de diferentes tipos son iguales; en cambio, comprueba si el lenguaje permite que el tipo de cualquiera de los operandos se convierta al tipo del otro y, si lo hace, compara el operando convertido con el no convertido. Tal comportamiento significa que para las variablesx
,y
yz
con algunas combinaciones de tipos, es posible tenerx==y
yy==z
perox!=z
[por ejemplo, x = 16777216f y = 16777216 z = 16777217]. También significa que la comparaciónI==i
se traduce como "Convertir I en anint
y, si eso no genera una excepción, compararlo coni
".fuente
==
de tal manera que si en todos los casosx==y
,y==z
yx==z
todo sin quejarse de compilación, las tres comparaciones se comportarán como una relación de equivalencia. Es curioso que los diseñadores impulsen todo tipo de funciones de lenguaje sofisticado, pero ignoren el cumplimiento axiomático.Es por la función de autoboxing de Javas . El compilador detecta que en el lado derecho de la comparación está usando un entero primitivo y necesita desempaquetar el valor entero envolvente en un valor int primitivo también.
Dado que eso no es posible (es nulo como se alineó),
NullPointerException
se lanza.fuente
En
i == 0
Java intentará hacer un desempaquetado automático y hacer una comparación numérica (es decir, "¿el valor almacenado en el objeto contenedor es referenciado pori
el mismo valor0
?").Dado que
i
esnull
el unboxing arrojará unNullPointerException
.El razonamiento es el siguiente:
La primera oración de JLS § 15.21.1 Operadores de igualdad numérica == y! = Se lee así:
Claramente
i
se puede convertir en un tipo numérico y0
es un tipo numérico, por lo que la promoción numérica binaria se realiza en los operandos.§ 5.6.2 Promoción numérica binaria dice (entre otras cosas):
§ 5.1.8 Unboxing Conversion dice (entre otras cosas):
fuente
Simplemente escriba un método y llámelo para evitar NullPointerException.
public static Integer getNotNullIntValue(Integer value) { if(value!=null) { return value; } return 0; }
fuente