Esto para mí suena como un problema razonablemente común que los desarrolladores junior a intermedios tienden a enfrentar en algún momento: o no saben o no confían en los contratos en los que están participando y sobreprotegen defensivamente los nulos. Además, cuando escriben su propio código, tienden a confiar en los valores nulos devueltos para indicar algo que requiere que la persona que llama compruebe los valores nulos.
Para decirlo de otra manera, hay dos casos en los que aparece la comprobación nula:
Donde nulo es una respuesta válida en términos del contrato; y
Donde no es una respuesta válida.
(2) es fácil. Utilice assert
sentencias (aserciones) o permita fallas (por ejemplo, NullPointerException ). Las aserciones son una característica de Java altamente infrautilizada que se agregó en 1.4. La sintaxis es:
assert <condition>
o
assert <condition> : <object>
donde <condition>
es una expresión booleana y <object>
es un objeto cuyo toString()
resultado del método se incluirá en el error.
Una assert
declaración arroja un Error
( AssertionError
) si la condición no es verdadera. Por defecto, Java ignora las aserciones. Puede habilitar aserciones pasando la opción -ea
a la JVM. Puede habilitar y deshabilitar aserciones para clases y paquetes individuales. Esto significa que puede validar el código con las aserciones durante el desarrollo y las pruebas, y deshabilitarlas en un entorno de producción, aunque mis pruebas casi no han demostrado un impacto en el rendimiento de las aserciones.
No usar aserciones en este caso está bien porque el código simplemente fallará, que es lo que sucederá si usa aserciones. La única diferencia es que con las afirmaciones puede ocurrir antes, de una manera más significativa y posiblemente con información adicional, lo que puede ayudarlo a descubrir por qué sucedió si no lo esperaba.
(1) es un poco más difícil. Si no tienes control sobre el código al que estás llamando, entonces estás atascado. Si nulo es una respuesta válida, debe verificarlo.
Sin embargo, si controlas el código (y este suele ser el caso), entonces es una historia diferente. Evite usar nulos como respuesta. Con los métodos que devuelven colecciones, es fácil: devolver colecciones vacías (o matrices) en lugar de nulos casi todo el tiempo.
Con no colecciones puede ser más difícil. Considere esto como un ejemplo: si tiene estas interfaces:
public interface Action {
void doSomething();
}
public interface Parser {
Action findAction(String userInput);
}
donde Parser toma datos sin procesar del usuario y encuentra algo que hacer, tal vez si está implementando una interfaz de línea de comando para algo. Ahora puede hacer que el contrato que devuelve sea nulo si no hay una acción adecuada. Eso lleva a la comprobación nula de la que estás hablando.
Una solución alternativa es no devolver nunca nulo y en su lugar usar el patrón de objeto nulo :
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();
}
a
ParserFactory.getParser().findAction(someInput).doSomething();
que es un diseño mucho mejor porque conduce a un código más conciso.
Dicho esto, quizás sea completamente apropiado que el método findAction () arroje una excepción con un mensaje de error significativo, especialmente en este caso en el que confía en la entrada del usuario. Sería mucho mejor para el método findAction lanzar una excepción que para que el método de llamada explote con una simple NullPointerException sin explicación.
try {
ParserFactory.getParser().findAction(someInput).doSomething();
} catch(ActionNotFoundException anfe) {
userConsole.err(anfe.getMessage());
}
O si cree que el mecanismo de prueba / captura es demasiado feo, en lugar de no hacer nada, su acción predeterminada debería proporcionar comentarios al usuario.
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);
}
}
}