Pregunta Evitando! = Declaraciones nulas


yo suelo object != null mucho para evitar NullPointerException.

¿Hay una buena alternativa a esto?

Por ejemplo:

if (someobject != null) {
    someobject.doCalc();
}

Esto evita una NullPointerException, cuando se desconoce si el objeto es null o no.

Tenga en cuenta que la respuesta aceptada puede estar desactualizada, consulte https://stackoverflow.com/a/2386013/12943 para un enfoque más reciente.


3548


origen


Respuestas:


Para mí, esto suena como un problema razonablemente común que los desarrolladores de nivel intermedio a intermedio tienden a enfrentar en algún momento: o desconocen o no confían en los contratos en los que participan y evalúan de manera defensiva los nulos. Además, cuando escriben su propio código, tienden a confiar en la devolución de nulos para indicar algo que requiere que la persona que llama verifique los nulos.

Para decirlo de otra manera, hay dos casos en los que aparece la comprobación nula:

  1. Donde null es una respuesta válida en términos del contrato; y

  2. Donde no es una respuesta válida.

(2) es fácil. Ya sea uso assert declaraciones (aserciones) o permitir la falla (por ejemplo, Excepción de puntero nulo) 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>

dónde <condition> es una expresión booleana y <object> es un objeto cuya toString() La salida del método se incluirá en el error.

Un assert declaración arroja un Error (AssertionError) si la condición no es verdadera. Por defecto, Java ignora las aserciones. Puede habilitar las afirmaciones pasando la opción -ea a la JVM. Puede habilitar y deshabilitar las aserciones para clases y paquetes individuales. Esto significa que puede validar el código con las aserciones durante el desarrollo y la prueba, y deshabilitarlas en un entorno de producción, aunque mis pruebas no han tenido impacto en el rendimiento de las aserciones.

No utilizar 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 podría suceder 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 estaba esperando.

(1) es un poco más difícil. Si no tienes control sobre el código al que llamas, entonces estás estancado. Si null es una respuesta válida, debes verificarla.

Sin embargo, si se trata de un código que controlas (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: devuelve colecciones vacías (o matrices) en lugar de nulos casi todo el tiempo.

Con las no colecciones, podría 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 la entrada del usuario sin procesar 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 devuelva nulo si no hay una acción apropiada. Eso lleva a la comprobación nula de la que estás hablando.

Una solución alternativa es nunca devolver 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, tal vez sea totalmente 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 arrojar una excepción que para el método de llamada para explotar con una simple NullPointerException sin explicación.

try {
    ParserFactory.getParser().findAction(someInput).doSomething();
} catch(ActionNotFoundException anfe) {
    userConsole.err(anfe.getMessage());
}

O si crees que el mecanismo de prueba / captura es demasiado feo, en lugar de no hacer nada, tu acción predeterminada debería proporcionar retroalimentación 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);
        }
    }
}

2389



Si usa (o planea usar) un IDE de Java como JetBrains IntelliJ IDEA, Eclipse o Netbeans o una herramienta como findbugs, entonces puedes usar anotaciones para resolver este problema.

Básicamente, tienes @Nullable y @NotNull.

Puede usar en método y parámetros, como este:

@NotNull public static String helloWorld() {
    return "Hello World";
}

o

@Nullable public static String helloWorld() {
    return "Hello World";
}

El segundo ejemplo no se compilará (en IntelliJ IDEA).

Cuando usas el primero helloWorld() función en otra pieza de código:

public static void main(String[] args)
{
    String result = helloWorld();
    if(result != null) {
        System.out.println(result);
    }
}

Ahora el compilador IntelliJ IDEA le dirá que el cheque es inútil, ya que el helloWorld() la función no regresará null, nunca.

Usando el parámetro

void someMethod(@NotNull someParameter) { }

si escribes algo como:

someMethod(null);

Esto no compilará

Último ejemplo usando @Nullable

@Nullable iWantToDestroyEverything() { return null; }

Haciendo esto

iWantToDestroyEverything().something();

Y puedes estar seguro de que esto no sucederá. :)

Es una buena manera de dejar que el compilador verifique algo más de lo normal y hacer cumplir sus contratos para que sea más fuerte. Desafortunadamente, no es compatible con todos los compiladores.

En IntelliJ IDEA 10.5 y en adelante, agregaron soporte para cualquier otro @Nullable  @NotNull implementaciones.

Ver la publicación de blog Anotaciones más flexibles y configurables @ Nullable / @ NotNull.


516



Si los valores nulos no están permitidos

Si su método se llama externamente, comience con algo como esto:

public void method(Object object) {
  if (object == null) {
    throw new IllegalArgumentException("...");
  }

Luego, en el resto de ese método, sabrás que object No es nulo.

Si se trata de un método interno (que no forma parte de una API), solo documente que no puede ser nulo, y eso es todo.

Ejemplo:

public String getFirst3Chars(String text) {
  return text.subString(0, 3);
}

Sin embargo, si su método solo pasa el valor, y el siguiente método lo pasa, etc. podría ser problemático. En ese caso, es posible que desee verificar el argumento como se indicó anteriormente.

Si se permite nulo

Esto realmente depende. Si encuentro que a menudo hago algo como esto:

if (object == null) {
  // something
} else {
  // something else
}

Entonces me ramifico y hago dos cosas completamente diferentes. No hay ningún fragmento de código feo, porque realmente necesito hacer dos cosas diferentes dependiendo de los datos. Por ejemplo, ¿debería trabajar en la entrada, o debería calcular un buen valor predeterminado?


En realidad, es raro para mí usar el modismo "if (object != null && ...".

Puede ser más fácil darle ejemplos, si muestra ejemplos de donde normalmente usa el modismo.


282



Vaya, casi odio agregar otra respuesta cuando tenemos 57 formas diferentes de recomendar la NullObject pattern, pero creo que a algunas personas interesadas en esta pregunta les gustaría saber que hay una propuesta sobre la mesa para que Java 7 agregue "manejo nulo seguro"-una sintaxis simplificada para la lógica if-not-equal-null.

El ejemplo dado por Alex Miller se ve así:

public String getPostcode(Person person) {  
  return person?.getAddress()?.getPostcode();  
}  

los ?. significa solo desmarcar el identificador izquierdo si no es nulo; de lo contrario, evalúe el resto de la expresión como null. Algunas personas, como el miembro de Java Posse Dick Wall y el votantes en Devoxx realmente me encanta esta propuesta, pero también hay oposición, con el argumento de que en realidad alentará un mayor uso de null como un valor centinela.


Actualizar: Un propuesta oficial para un operador nulo seguro en Java 7 se ha presentado bajo Proyecto Coin. La sintaxis es un poco diferente al ejemplo anterior, pero es la misma noción.


Actualizar: La propuesta del operador de seguridad nula no se convirtió en Project Coin. Por lo tanto, no verá esta sintaxis en Java 7.


215



Si los valores indefinidos no están permitidos:

Puede configurar su IDE para advertirle sobre la eliminación de referencias nula potencial. P.ej. en Eclipse, ver Preferencias> Java> Compilador> Errores / Advertencias / Análisis nulo.

Si los valores indefinidos están permitidos:

Si quiere definir una nueva API donde los valores indefinidos tienen sentido, utilizar el Patrón de opción (Puede ser familiar de los lenguajes funcionales). Tiene las siguientes ventajas:

  • Se establece explícitamente en la API si existe o no una entrada o salida.
  • El compilador lo obliga a manejar el caso "indefinido".
  • La opción es una mónada, por lo que no es necesario realizar una verificación nula detallada, solo use map / foreach / getOrElse o un combinador similar para usar el valor de forma segura (ejemplo).

Java 8 tiene un built-in Optional clase (recomendado); para versiones anteriores, hay alternativas de biblioteca, por ejemplo Guayabaes Optional o FunctionalJavaes Option. Pero al igual que muchos patrones de estilo funcional, el uso de Opción en Java (incluso 8) da como resultado un buen resumen, que puede reducir utilizando un lenguaje JVM menos detallado, p. Scala o Xtend.

Si tiene que lidiar con una API que puede devolver valores nulos, no se puede hacer mucho en Java. Xtend y Groovy tienen el Operador de Elvis  ?: y el operador de desreferencia nulo seguro  ?., pero tenga en cuenta que esto devuelve nulo en caso de una referencia nula, por lo que simplemente "difiere" el manejo adecuado de nulo.


177



Solo para esta situación -

No se comprueba si una variable es nula antes de invocar un método equals (un ejemplo de comparación de cadenas a continuación):

if ( foo.equals("bar") ) {
 // ...
}

dará como resultado un NullPointerException Si foo no existe

Puede evitar eso si compara su Strings como este:

if ( "bar".equals(foo) ) {
 // ...
}

160



Con Java 8 viene el nuevo java.util.Optional clase que posiblemente resuelve parte del problema. Al menos se puede decir que mejora la legibilidad del código y, en el caso de las API públicas, hace que el contrato de la API sea más claro para el desarrollador del cliente.

Ellos trabajan así:

Un objeto opcional para un tipo dado (Fruit) se crea como el tipo de devolución de un método. Puede estar vacío o contener un Fruit objeto:

public static Optional<Fruit> find(String name, List<Fruit> fruits) {
   for (Fruit fruit : fruits) {
      if (fruit.getName().equals(name)) {
         return Optional.of(fruit);
      }
   }
   return Optional.empty();
}

Ahora mira este código donde buscamos una lista de Fruit (fruits) para una instancia de Fruta dada:

Optional<Fruit> found = find("lemon", fruits);
if (found.isPresent()) {
   Fruit fruit = found.get();
   String name = fruit.getName();
}

Puedes usar el map() operador para realizar un cálculo en - o extraer un valor de - un objeto opcional. orElse() le permite proporcionar una alternativa para los valores perdidos.

String nameOrNull = find("lemon", fruits)
    .map(f -> f.getName())
    .orElse("empty-name");

Por supuesto, la comprobación del valor nulo / vacío sigue siendo necesaria, pero al menos el desarrollador es consciente de que el valor puede estar vacío y el riesgo de olvidarse de comprobar es limitado.

En una API construida desde cero usando Optional cada vez que un valor de retorno puede estar vacío, y devolver un objeto simple solo cuando no puede ser null (convención), el código del cliente puede abandonar las comprobaciones nulas en los valores de retorno de objetos simples ...

Por supuesto Optional también podría usarse como un argumento de método, quizás una mejor manera de indicar argumentos opcionales que 5 o 10 métodos de sobrecarga en algunos casos.

Optional ofrece otros métodos convenientes, como orElse que permiten el uso de un valor predeterminado, y ifPresent eso funciona con expresiones lambda.

Los invito a leer este artículo (mi fuente principal para escribir esta respuesta) en el que NullPointerException (y en general puntero nulo) problemática, así como la solución (parcial) presentada por Optional están bien explicados: Objetos opcionales de Java.


135



Dependiendo del tipo de objetos que esté verificando, puede usar algunas de las clases en Apache commons, como: Apache commons lang y colecciones de apache commons

Ejemplo:

String foo;
...
if( StringUtils.isBlank( foo ) ) {
   ///do something
}

o (dependiendo de lo que necesite comprobar):

String foo;
...
if( StringUtils.isEmpty( foo ) ) {
   ///do something
}

La clase StringUtils es solo una de muchas; hay bastantes buenas clases en los comunes que anulan la manipulación segura.

A continuación se incluye un ejemplo de cómo puede usar la vallidación nula en JAVA cuando incluye la biblioteca apache (commons-lang-2.4.jar)

public DOCUMENT read(String xml, ValidationEventHandler validationEventHandler) {
    Validate.notNull(validationEventHandler,"ValidationHandler not Injected");
    return read(new StringReader(xml), true, validationEventHandler);
}

Y si está utilizando Spring, Spring también tiene la misma funcionalidad en su paquete, consulte la biblioteca (spring-2.4.6.jar)

Ejemplo sobre cómo usar este classf estático de spring (org.springframework.util.Assert)

Assert.notNull(validationEventHandler,"ValidationHandler not Injected");

113