Melhore a usabilidade dos NullPointerExceptions gerados pela JVM

Por Gaspar Barancelli Junior em 22 de fevereiro de 2023

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 getN() porque o retorno do valor de A.getB() é nulo, sendo assim fica claro que a variável B era nula.

Caso não tenham sido exibidos os detalhes nos logs do NPE, você pode adicionar a seguinte flag na JVM.

-XX:+ShowCodeDetailsInExceptionMessages

// Livros recomendados relacionados ao assunto do post

// Compartilhe esse Post