Isso para mim parece um problema razoavelmente comum que os desenvolvedores juniores a intermediários tendem a enfrentar em algum momento: eles não sabem ou não confiam nos contratos dos quais estão participando e buscam na defensiva por nulos. Além disso, ao escrever seu próprio código, eles tendem a confiar no retorno de nulos para indicar algo, exigindo que o chamador verifique nulos.
Em outras palavras, há duas instâncias em que a verificação nula é exibida:
Onde nulo é uma resposta válida em termos do contrato; e
Onde não é uma resposta válida.
(2) é fácil. Use assert
instruções (asserções) ou permita falha (por exemplo, NullPointerException ). As asserções são um recurso Java altamente subutilizado que foi adicionado na 1.4. A sintaxe é:
assert <condition>
ou
assert <condition> : <object>
onde <condition>
é uma expressão booleana e <object>
é um objeto cuja toString()
saída do método será incluída no erro.
Uma assert
instrução lança um Error
( AssertionError
) se a condição não for verdadeira. Por padrão, Java ignora asserções. Você pode ativar asserções passando a opção -ea
para a JVM. Você pode ativar e desativar asserções para classes e pacotes individuais. Isso significa que você pode validar o código com as asserções durante o desenvolvimento e o teste e desabilitá-las em um ambiente de produção, embora meus testes tenham demonstrado quase nenhum impacto no desempenho das asserções.
Não usar afirmações nesse caso é bom porque o código falhará, o que acontecerá se você usar afirmações. A única diferença é que, com afirmações, isso pode acontecer mais cedo, de maneira mais significativa e possivelmente com informações extras, o que pode ajudá-lo a descobrir por que isso aconteceu se você não estava esperando.
(1) é um pouco mais difícil. Se você não tem controle sobre o código para o qual está ligando, está preso. Se nulo é uma resposta válida, você deve procurá-la.
No entanto, se é o código que você controla (e esse geralmente é o caso), é uma história diferente. Evite usar nulos como resposta. Com métodos que retornam coleções, é fácil: retornar coleções vazias (ou matrizes) em vez de nulos praticamente o tempo todo.
Com as não coleções, pode ser mais difícil. Considere isso como um exemplo: se você possui estas interfaces:
public interface Action {
void doSomething();
}
public interface Parser {
Action findAction(String userInput);
}
onde o Analisador pega a entrada bruta do usuário e encontra algo para fazer, talvez se você estiver implementando uma interface de linha de comando para algo. Agora você pode fazer com que o contrato retorne nulo se não houver uma ação apropriada. Isso leva à verificação nula da qual você está falando.
Uma solução alternativa é nunca retornar nulo e, em vez disso, usar o padrão Null Object :
public class MyParser implements Parser {
private static Action DO_NOTHING = new Action() {
public void doSomething() { /* do nothing */ }
};
public Action findAction(String userInput) {
// ...
if ( /* we can't find any actions */ ) {
return DO_NOTHING;
}
}
}
Comparar:
Parser parser = ParserFactory.getParser();
if (parser == null) {
// now what?
// this would be an example of where null isn't (or shouldn't be) a valid response
}
Action action = parser.findAction(someInput);
if (action == null) {
// do nothing
} else {
action.doSomething();
}
para
ParserFactory.getParser().findAction(someInput).doSomething();
que é um design muito melhor porque leva a um código mais conciso.
Dito isso, talvez seja inteiramente apropriado que o método findAction () lance uma exceção com uma mensagem de erro significativa - especialmente nesse caso em que você depende da entrada do usuário. Seria muito melhor para o método findAction lançar uma exceção do que para o método de chamada explodir com uma simples NullPointerException sem explicação.
try {
ParserFactory.getParser().findAction(someInput).doSomething();
} catch(ActionNotFoundException anfe) {
userConsole.err(anfe.getMessage());
}
Ou, se você acha que o mecanismo de tentativa / captura é muito feio, em vez de Não fazer nada, sua ação padrão deve fornecer feedback ao usuário.
public Action findAction(final String userInput) {
/* Code to return requested Action if found */
return new Action() {
public void doSomething() {
userConsole.err("Action not found: " + userInput);
}
}
}