Eu tenho este código:
package tests;
import java.util.Hashtable;
public class Tests {
public static void main(String[] args) {
Hashtable<String, Boolean> modifiedItems = new Hashtable<String, Boolean>();
System.out.println("TEST 1");
System.out.println(modifiedItems.get("item1")); // Prints null
System.out.println("TEST 2");
System.out.println(modifiedItems.get("item1") == null); // Prints true
System.out.println("TEST 3");
System.out.println(Boolean.valueOf(null)); // Prints false
System.out.println("TEST 4");
System.out.println(Boolean.valueOf(modifiedItems.get("item1"))); // Produces NullPointerException
System.out.println("FINISHED!"); // Never executed
}
}
Meu problema é que não entendo por que o Teste 3 funciona bem (imprime false
e não produz NullPointerException
), enquanto o Teste 4 gera a NullPointerException
. Como você pode ver nos testes 1 e 2 , null
e modifiedItems.get("item1")
são iguais a e null
.
O comportamento é o mesmo em Java 7 e 8.
java
nullpointerexception
boolean
David E
fonte
fonte
null
para a mesma função não gera um NPE! Há uma boa razão para isso, mas certamente é confuso à primeira vista :-)==
é aplicado.Respostas:
Você deve observar cuidadosamente qual sobrecarga está sendo invocada:
Boolean.valueOf(null)
está invocandoBoolean.valueOf(String)
. Isso não lança umNPE
mesmo se fornecido com um parâmetro nulo.Boolean.valueOf(modifiedItems.get("item1"))
está chamandoBoolean.valueOf(boolean)
, porquemodifiedItems
os valores de são do tipoBoolean
, que requer uma conversão de unboxing. Uma vez quemodifiedItems.get("item1")
énull
, é o unboxing desse valor - não oBoolean.valueOf(...)
- que lança o NPE.As regras para determinar qual sobrecarga é invocada são muito complicadas , mas são mais ou menos assim:
Em uma primeira passagem, uma correspondência de método é pesquisada sem permitir boxing / unboxing (nem métodos de aridade variável).
null
é um valor aceitável para a,String
mas nãoboolean
,Boolean.valueOf(null)
é correspondidoBoolean.valueOf(String)
nesta passagem;Boolean
não é aceitável paraBoolean.valueOf(String)
ouBoolean.valueOf(boolean)
, portanto, nenhum método é correspondido nesta passagem paraBoolean.valueOf(modifiedItems.get("item1"))
.Em uma segunda passagem, uma correspondência de método é pesquisada, permitindo boxing / unboxing (mas ainda não métodos de aridade variável).
Boolean
pode ser desempacotadoboolean
, entãoBoolean.valueOf(boolean)
é correspondidoBoolean.valueOf(modifiedItems.get("item1"))
nesta passagem; mas uma conversão unboxing deve ser inserida pelo compilador para invocá-lo:Boolean.valueOf(modifiedItems.get("item1").booleanValue())
(Há uma terceira passagem que permite métodos de aridade variável, mas isso não é relevante aqui, pois as duas primeiras passagens corresponderam a esses casos)
fonte
Boolean.valueOf(modifiedItems.get("item1").booleanValue())
no código-fonte em vez deBoolean.valueOf(modifiedItems.get("item1"))
?.booleanValue()
enterrado na expressão. Duas observações: 1) o (des) boxing automático é um recurso deliberado do Java para remover problemas sintáticos; fazer você mesmo é possível, mas não idiomático; 2) isso não ajuda em nada - certamente não impede que o problema ocorra, nem fornece nenhuma informação extra quando a falha ocorre (o rastreamento da pilha seria idêntico, porque o código executado é idêntico).Como
modifiedItems.get
retorna aBoolean
(que não pode ser convertido em aString
), a assinatura que seria usada éBoolean.valueOf(boolean)
, onde oBoolean
é enviado para uma primitivaboolean
. Uma vez quenull
é devolvido lá, a caixa de saída falha com aNullPointerException
.fonte
Assinatura do método
O método
Boolean.valueOf(...)
possui duas assinaturas:public static Boolean valueOf(boolean b)
public static Boolean valueOf(String s)
Seu
modifiedItems
valor éBoolean
. Você não pode lançarBoolean
paraString
portanto, a primeira assinatura será escolhidaUnboxing booleano
Em sua declaração
que pode ser lido como
No entanto,
modifiedItems.get("item1")
retorna ,null
então você basicamente teráo que obviamente leva a um
NullPointerException
fonte
Como Andy já descreveu muito bem o motivo de
NullPointerException
:que se deve ao desencaixotamento booleano:
seja convertido em:
em tempo de execução e, em seguida, lança
NullPointerException
ifmodifiedItems.get("item1")
é nulo.Agora, eu gostaria de adicionar mais um ponto aqui que o desempacotamento das classes a seguir em suas respectivas primitivas também pode produzir
NullPointerException
exceção se seus objetos retornados correspondentes forem nulos.Aqui está o código:
fonte
Uma maneira de entender isso é quando
Boolean.valueOf(null)
é invocado, java está precisamente sendo informado para avaliar null.No entanto, quando
Boolean.valueOf(modifiedItems.get("item1"))
é invocado, java é instruído a obter um valor do HashTable do tipo de objeto Boolean, mas não encontra o tipo Boolean, ele encontra um beco sem saída (null), embora espere um Boolean. A exceção NullPointerException é lançada porque os criadores desta parte do java decidiram que essa situação é uma instância de algo que está dando errado no programa e que precisa da atenção do programador. (Aconteceu algo não intencional.)Nesse caso, é mais a diferença entre declarar deliberadamente que você pretendia que o nulo estivesse lá e java encontrar uma referência ausente para um objeto (nulo) onde um objeto deveria ser encontrado.
Veja mais informações sobre NullPointerException nesta resposta: https://stackoverflow.com/a/25721181/4425643
fonte