Melhore a usabilidade dos NullPointerExceptions gerados pela JVM
Todo desenvolvedor Java encontrou NullPointerExceptions (NPEs). Uma vez que os NPEs podem ocorrer em quase qualquer lugar em um programa, geralmente é impraticável tentar capturá-los e recuperá-los. Como resultado, os desenvolvedores contam com a JVM para identificar a origem de um NPE quando ele realmente ocorre. Por exemplo, suponha que ocorra um NPE neste código:
class A {
B b;
public B getB() {
return b;
}
}
class B {
public Integer getN() {
return 10;
}
}
public class Teste {
public static void main(String[] args) {
var a = new A();
System.out.println(a.getB().getN());
}
}
Ao executarmos a classe acima em uma versão do Java 13-, a JVM imprimirá o nome do arquivo e o número da linha que causou o NPE.
Exception in thread "main" java.lang.NullPointerException at Teste.main(Teste.java:22)
Observando os logs não saberíamos ao certo qual objeto era nulo, pois existe uma chamada encadeada (a → b → n), sendo assim, não temos como identificar qual dos 3 objetos era nulo, apenas depurando nossa aplicação conseguiríamos descobrir.
A dificuldade em analisar NPEs pelos logs, foi a grande motivação para a elaboração da JEP 358: Helpful NullPointerExceptions, que foi implementada e disponibiliza no OpenJDK 14.
Para contextualizar, vamos entender o que é um JEP e visualizar um trecho da descrição de implementação da proposta de aprimoramento de NEPs:
JEP - JDK Enhancement Proposal
A proposta de aprimoramento do JDK é um processo elaborado pela Oracle Corporation para coletar propostas de aprimoramentos no Java Development Kit e no OpenJDK. Nas palavras do Oracle, o JEP serve como um roteiro de longo prazo para projetos de liberação do JDK e esforços relacionados.
JEP 358: Helpful NullPointerExceptions
A JVM lança um NullPointerException(NPE) no ponto em um programa em que o código tenta cancelar a referência de uma referência nulla. Ao analisar as instruções de bytecode do programa, a JVM determinará precisamente qual variável era nulla e descreverá a variável (em termos de código-fonte) com uma mensagem com detalhes nulos no NPE. A mensagem de detalhe nulo será então mostrada na mensagem da JVM, junto com o método, nome do arquivo e número da linha.
Agora sim, utilizando uma versão do Java 14+, vamos executar novamente a classe de testes desenvolvida no inicio desse documento, para que possamos analisar a diferença nos logs.
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "B.getN()" because the return value of "A.getB()" is null
at Teste.main(Teste.java:22)
Analisando a mensagem de log, sabemos que a JVM não pode invocar o método
porque o retorno do valor de getN()
é nulo, sendo assim fica claro que a variável B era nula.A.getB()
Caso não tenham sido exibidos os detalhes nos logs do NPE, você pode adicionar a seguinte flag na JVM.
-XX:+ShowCodeDetailsInExceptionMessages