Evitar! = Declaraciones nulas

4016

Yo uso object != nullmucho para evitar NullPointerException.

¿Hay una buena alternativa a esto?

Por ejemplo, a menudo uso:

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

Esto comprueba una NullPointerExceptionpara el someobjectobjeto en el fragmento anterior.

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

Goran Martinic
fuente
120
@Shervin Alentar nulos hace que el código sea menos comprensible y menos confiable.
Tom Hawtin - tackline
44
Se propusieron los operadores de Elvis, pero parece que no estará en Java 7 . Demasiado, ?. ?: y? [] son ​​increíbles ahorradores de tiempo.
Scott
72
No usar null es superior a la mayoría de las otras sugerencias aquí. Lanza excepciones, no devuelvas ni permitas nulos. Por cierto, la palabra clave 'afirmar' es inútil, porque está deshabilitada de forma predeterminada. Utilice un mecanismo de falla siempre habilitado
ianpojman
13
Esta es una razón por la que ahora estoy usando Scala. En Scala no todo es anulable. Si desea permitir pasar o devolver "nada", debe usar explícitamente la Opción [T] en lugar de solo el argumento T als o el tipo de retorno.
Thekwasti
11
@thSoft De hecho, el Optiontipo impresionante de Scala se ve empañado por la falta de voluntad del lenguaje para controlar o rechazar null. Mencioné esto en hackernews y me votaron negativamente y me dijeron que "nadie usa nulo en Scala de todos modos". Adivina qué, ya estoy encontrando nulos en Scala escrito por compañeros de trabajo. Sí, lo están "haciendo mal" y deben ser educados al respecto, pero el hecho sigue siendo que el sistema de tipos del lenguaje debería protegerme de esto y no lo hace :(
Andres F.

Respuestas:

2644

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:

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

  2. Donde no es una respuesta válida.

(2) es fácil. Utilice assertsentencias (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 assertdeclaració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 -eaa 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);
        }
    }
}
cletus
fuente
631
No estoy de acuerdo con sus declaraciones para la acción DO_NOTHING. Si el método find action no puede encontrar una acción, entonces devolver nulo es lo correcto. Ha "encontrado" una acción en su código que realmente no se encuentra, lo que viola el principio del método, para encontrar una acción utilizable.
MetroidFan2002
266
Estoy de acuerdo en que nulo se usa en exceso en Java, especialmente con las listas. Muchos apis serían mejores si devuelven una lista / matriz / colección vacía en lugar de nula. Muchas veces, nulo se usa donde se debe lanzar una excepción. Se debe lanzar una excepción si el analizador no puede analizar.
Laplie Anderson
92
El último ejemplo aquí es, IIRC, el patrón de diseño de objeto nulo.
Steven Evers
6262
@Cshah (y MetroidFan2002). Simple, ponga eso en el contrato y luego está claro que una acción devuelta no encontrada no hará nada. Si esta es información importante para la persona que llama, proporcione una forma de descubrir que fue una acción no encontrada (es decir, proporcione un método para verificar si el resultado fue el objeto DO_NOTHING). Alternativamente, si normalmente se encuentra la acción, entonces aún no debe devolver nulo, sino que debe lanzar una excepción que indique específicamente esa condición; esto todavía da como resultado un mejor código. Si lo desea, proporcione un método separado que devuelva boolean para verificar si existe acción.
Kevin Brock
35
Conciso no es igual al código de calidad. Lamento que pienses eso. Su código oculta situaciones en las que un error sería ventajoso.
gshauger
635

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

Básicamente, tienes @Nullabley @NotNull.

Puede usar métodos 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 usa la primera helloWorld()función en otro 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 la verificación es inútil, ya que la helloWorld()función no volverá nullnunca.

Usando parámetro

void someMethod(@NotNull someParameter) { }

si escribes algo como:

someMethod(null);

Esto no se compilará.

Último ejemplo usando @Nullable

@Nullable iWantToDestroyEverything() { return null; }

Haciendo esto

iWantToDestroyEverything().something();

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

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

En IntelliJ IDEA 10.5 y posteriores, agregaron soporte para cualquier otra @Nullable @NotNullimplementación.

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

Luca Molteni
fuente
120
@NotNull, @Nullabley otras anotaciones de nulidad son parte de JSR 305 . También puede usarlos para detectar posibles problemas con herramientas como FindBugs .
Jacek S
32
Me resulta extrañamente molesto que las interfaces @NotNull& @Nullablevivan en el paquete com.sun.istack.internal. (Supongo que asocio com.sun con advertencias sobre el uso de una API patentada).
Jonik
20
la portabilidad del código se anula con los jetbrains. Pensaría dos veces (al cuadrado) antes de atar al nivel ide. Como Jacek S dijo que son parte de JSR de cualquier manera, por cierto, pensé que era JSR303.
Java Ka Baby
10
Realmente no creo que usar un compilador personalizado sea una solución viable para este problema.
Shivan Dragon
64
Lo bueno de las anotaciones, que son @NotNully @Nullablees que se degradan muy bien cuando el código fuente está construido por un sistema que no las comprende. Por lo tanto, en efecto, el argumento de que el código no es portátil puede no ser válido: si usa un sistema que admite y comprende estas anotaciones, obtendrá el beneficio adicional de una verificación de errores más estricta, de lo contrario obtendrá menos, pero su código aún debería construir bien, y la calidad de su programa en ejecución es LA MISMA, porque estas anotaciones no se aplicaron en tiempo de ejecución de todos modos. Además, todos los compiladores son personalizados ;-)
amn
321

Si no se permiten valores nulos

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 objectno es nulo.

Si es un método interno (no 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 transfiere el valor y el siguiente método lo transfiere, etc., podría ser problemático. En ese caso, es posible que desee verificar el argumento como se indica arriba.

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 un fragmento de código feo, porque realmente necesito hacer dos cosas diferentes dependiendo de los datos. Por ejemplo, ¿debo trabajar en la entrada o debo calcular un buen valor predeterminado?


En realidad, es raro que use el idioma " if (object != null && ...".

Puede ser más fácil darle ejemplos, si muestra ejemplos de dónde usa habitualmente el idioma.

myplacedk
fuente
94
¿Cuál es el punto de lanzar IllegalArgumentException? Creo que NullPointerException sería más claro, y eso también se generaría si no realiza la comprobación nula usted mismo. Yo usaría afirmar o nada en absoluto.
Axel
23
Es poco probable que cualquier otro valor que no sea nulo sea aceptable. Podría tener IllegalArgumentException, OutOfRageException, etc., etc. A veces esto tiene sentido. Otras veces terminas creando muchas clases de excepción que no agregan ningún valor, entonces solo usas IllegalArgumentException. No tiene sentido tener una excepción para entrada nula y otra para todo lo demás.
myplacedk
77
Sí, estoy de acuerdo con el principio de falla rápida, pero en el ejemplo anterior, el valor no se transfiere, sino que es el objeto sobre el que se llamará a un método. Por lo tanto, falla igualmente rápido, y agregar un cheque nulo solo para lanzar una excepción que se lanzaría de todos modos al mismo tiempo y lugar no parece facilitar la depuración.
Axel
8
¿Un agujero de seguridad? El JDK está lleno de código como este. Si no desea que el usuario vea los stacktraces, desactívelos. Nadie implica que el comportamiento no esté documentado. MySQL está escrito en C donde desreferenciar punteros nulos es un comportamiento indefinido, nada como lanzar una excepción.
fgb
8
throw new IllegalArgumentException("object==null")
Thorbjørn Ravn Andersen
238

Wow, 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 Java 7 para agregar "null-safe manejo ": una sintaxis simplificada para la lógica si no es igual a nula.

El ejemplo dado por Alex Miller se ve así:

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

Los ?.medios solo desreferencian el identificador izquierdo si no es nulo; de lo contrario, evalúe el resto de la expresión como null. Algunas personas, como Dick Wall, miembro de Java Posse, y los votantes de Devoxx realmente adoran esta propuesta, pero también hay oposición, alegando que en realidad fomentará un mayor uso nullcomo valor centinela.


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


Actualización: la propuesta del operador nulo-seguro no se convirtió en Project Coin. Por lo tanto, no verá esta sintaxis en Java 7.

erickson
fuente
20
Creo que esto está mal. Debe haber una manera de especificar que una variable dada SIEMPRE sea no nula.
Thorbjørn Ravn Andersen
55
Actualización: la propuesta no hará Java7. Ver blogs.sun.com/darcy/entry/project_coin_final_five .
Boris Terzic
99
Idea interesante pero la elección de la sintaxis es absurda; No quiero una base de código llena de signos de interrogación anclados en cada articulación.
Rob
77
Este operador existe en Groovy , por lo que aquellos que quieran usarlo todavía lo tienen como una opción.
Muhd
8
Esta es la idea más ingeniosa que he visto. Debe agregarse a cada lenguaje sensible de la sintaxis de C. Prefiero "poner signos de interrogación" en todas partes que desplazarme a través de una pantalla llena de líneas o esquivar "cláusulas de guardia" todo el día.
Victor
196

Si no se permiten valores indefinidos:

Puede configurar su IDE para advertirle sobre posibles desreferencias nulas. Por ejemplo, en Eclipse, vea Preferencias> Java> Compilador> Errores / Advertencias / Análisis nulo .

Si se permiten valores indefinidos:

Si desea definir una nueva API donde los valores indefinidos tienen sentido , use el Patrón de opción (puede ser familiar a partir de 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 hay necesidad de una verificación nula detallada, solo use map / foreach / getOrElse o un combinador similar para usar el valor de manera segura (ejemplo) .

Java 8 tiene una Optionalclase incorporada (recomendado); para las versiones anteriores, hay alternativas de la biblioteca, por ejemplo guayaba 's Optionalo FunctionalJava ' s Option. Pero al igual que muchos patrones de estilo funcional, el uso de Opción en Java (incluso 8) da como resultado un poco de repetitivo, que puede reducir utilizando un lenguaje JVM menos detallado, por ejemplo, Scala o Xtend.

Si tiene que lidiar con una API que podría devolver nulos , no puede hacer mucho en Java. Xtend y Groovy tienen el operador de Elvis ?: y el operador de anulación de referencia de seguridad nula ?. , pero tenga en cuenta que esto devuelve nulo en caso de una referencia nula, por lo que simplemente "difiere" el manejo adecuado de nulo.

thSoft
fuente
22
De hecho, el patrón de opción es impresionante. Existen algunos equivalentes de Java. La guayaba contiene una versión limitada de esto llamada Opcional que omite la mayoría de las cosas funcionales. En Haskell, este patrón se llama Quizás.
Ben Hardy
99
Una clase opcional estará disponible en Java 8
Pierre Henry
1
... y todavía no tiene map ni flatMap: download.java.net/jdk8/docs/api/java/util/Optional.html
thSoft
3
El patrón opcional no resuelve nada; en lugar de un objeto potencialmente nulo, ahora tiene dos.
Boann el
1
@Boann, si se usa con cuidado, resuelve todos sus problemas de NPE. Si no, entonces supongo que hay un problema de "uso".
Louis F.
189

Solo para esta situación:

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

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

resultará en un NullPointerExceptionif foono existe.

Puede evitar eso si compara sus Stringmensajes de esta manera:

if ( "bar".equals(foo) ) {
 // ...
}
echox
fuente
42
Estoy de acuerdo, solo en esa situación. No soporto a los programadores que han llevado esto al siguiente nivel innecesario y escriben si (null! = MyVar) ... ¡simplemente me parece feo y no sirve para nada!
Alex Worden
20
Este es un ejemplo particular, probablemente el más utilizado, de una buena práctica general: si lo sabe, siempre hágalo <object that you know that is not null>.equals(<object that might be null>);. Funciona para otros métodos que no sean equalssi conoce el contrato y esos métodos pueden manejar nullparámetros.
Stef
99
Este es el primer ejemplo que he visto de las condiciones de Yoda que realmente tiene sentido
Erin Drummond, el
66
Se lanzan NullPointerExceptions por una razón. Se lanzan porque un objeto es nulo donde no debería estar. Es tarea del programador ARREGLAR esto, no OCULTAR el problema.
Oliver Watkins,
1
Try-Catch-Do Nada oculta el problema, esta es una práctica válida para evitar el azúcar perdido en el idioma.
echox
167

Con Java 8 viene la nueva java.util.Optionalclase que podría resolver 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í:

Se Fruitcrea un objeto opcional para un tipo dado ( ) como el tipo de retorno de un método. Puede estar vacío o contener un Fruitobjeto:

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 mire este código donde buscamos una lista de Fruit( fruits) para una instancia de Fruit dada:

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

Puede usar el map()operador para realizar un cálculo en un objeto opcional o extraer un valor de este. orElse()le permite proporcionar una reserva para los valores faltantes.

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

Por supuesto, la verificació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 olvidar verificar es limitado.

En una API construida desde cero utilizando Optionalsiempre que un valor de retorno puede estar vacío, y devolviendo un objeto plano solo cuando no puede ser null(convención), el código del cliente puede abandonar las verificaciones nulas en valores de retorno de objetos simples ...

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

Optionalofrece otros métodos convenientes, como el orElseque permite el uso de un valor predeterminado y ifPresentque funciona con expresiones lambda .

Los invito a leer este artículo (mi fuente principal para escribir esta respuesta) en el que la NullPointerExceptionsolución problemática (y en general puntero nulo) así como la solución (parcial) presentada Optionalestán bien explicadas: objetos opcionales Java .

Pierre Henry
fuente
11
La guayaba de Google tiene una implicación opcional para Java 6+.
Bradley Gottfried
13
Es muy importante enfatizar que el uso de Opcional solo con ifPresent () no agrega mucho valor por encima de la comprobación nula normal. Su valor central es que es una mónada que se puede usar en cadenas de funciones de map / flapMap, que logra resultados similares al operador de Elvis en Groovy mencionado en otra parte. Sin embargo, incluso sin este uso, encuentro que la sintaxis orElse / orElseThrow también es muy útil.
Cornel Masson
Este blog tiene una buena entrada en Winterbe.com/posts/2015/03/15/avoid-null-checks-in-java
JohnC
44
¿Por qué la gente tiene tendencia a hacer esto en if(optional.isPresent()){ optional.get(); }lugar de?optional.ifPresent(o -> { ...})
Satyendra Kumar
1
Por lo tanto, aparte de las sugerencias contractuales de API, en realidad solo se trata de atender a los programadores funcionales que disfrutan encadenar los métodos sin fin.
aplastar
125

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

Ejemplo:

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

o (según lo que necesite verificar):

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

La clase StringUtils es solo una de muchas; Hay bastantes buenas clases en los comunes que hacen una manipulación segura nula.

Aquí sigue un ejemplo de cómo puede usar la validació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");
javamonkey79
fuente
55
También puede usar la versión más genérica de Apache Commons, bastante útil al comienzo de los métodos para verificar los parámetros que encuentro. Validate.notNull (objeto, "el objeto no debe ser nulo"); commons.apache.org/lang/apidocs/org/apache/commons/lang/…
monojohnny
@monojohnny Validate usa declaraciones de afirmación en? Pregunto eso porque Assert puede activarse / desactivarse en JVM y se sugiere no usarlo en producción.
Kurapika
No lo creo, creo que solo arroja una RuntimeException si la validación falla
monojohnny
96
  • Si considera que un objeto no debe ser nulo (o es un error), use una aserción.
  • Si su método no acepta parámetros nulos, dígalo en el javadoc y use una aserción.

Debe verificar el objeto! = Nulo solo si desea manejar el caso donde el objeto puede ser nulo ...

Hay una propuesta para agregar nuevas anotaciones en Java7 para ayudar con los parámetros nulos / no nulos: http://tech.puredanger.com/java7/#jsr308

pgras
fuente
91

Soy fanático del código de "falla rápida". Pregúntese: ¿está haciendo algo útil en el caso de que el parámetro sea nulo? Si no tiene una respuesta clara de lo que debe hacer su código en ese caso ... Es decir, nunca debe ser nulo en primer lugar, luego ignórelo y permita que se arroje una NullPointerException. El código de llamada tendrá tanto sentido de un NPE como lo haría con una IllegalArgumentException, pero será más fácil para el desarrollador depurar y comprender qué salió mal si se lanza un NPE en lugar de que su código intente ejecutar alguna otra contingencia inesperada lógica, que en última instancia da como resultado que la aplicación falle de todos modos.

Alex Worden
fuente
2
es mejor usar aserciones, es decir, Contract.notNull (abc, "abc no debe ser nulo, ¿no se pudo cargar durante xyz?"); - esta es una forma más compacta que hacer un if (abc! = null) {lanzar nueva RuntimeException ...}
ianpojman
76

El marco de colecciones de Google ofrece una forma buena y elegante de lograr la verificación nula.

Hay un método en una clase de biblioteca como este:

static <T> T checkNotNull(T e) {
   if (e == null) {
      throw new NullPointerException();
   }
   return e;
}

Y el uso es (con import static):

...
void foo(int a, Person p) {
   if (checkNotNull(p).getAge() > a) {
      ...
   }
   else {
      ...
   }
}
...

O en tu ejemplo:

checkNotNull(someobject).doCalc();
usuario2427
fuente
71
mmm, ¿cuál es la diferencia? p.getAge () arrojaría el mismo NPE con menos sobrecarga y un seguimiento de pila más claro. ¿Qué me estoy perdiendo?
mysomic
15
Es mejor lanzar una IllegalArgumentException ("e == null") en su ejemplo, ya que indica claramente que se trata de una excepción prevista por el programador (junto con información suficiente para permitir que el encargado de la identificación identifique el problema). NullPointerExceptions debe reservarse para la JVM, ya que indica claramente que esto no fue intencional (y generalmente ocurre en un lugar difícil de identificar)
Thorbjørn Ravn Andersen
66
Esto ahora es parte de Google Guava.
Steven Benitez
32
A mí me huele a ingeniería excesiva. Simplemente deje que la JVM arroje un NPE y no desordene su código con esta basura.
Alex Worden
55
Me gusta y abro la mayoría de los métodos y constructores con comprobaciones explícitas de los argumentos; si hay un error, los métodos siempre fallan en las primeras líneas y conozco la referencia ofensiva sin encontrar algo comogetThing().getItsThing().getOtherThing().wowEncapsulationIsBroken().setLol("hi");
Cory Kendall el
75

A veces, tiene métodos que operan en sus parámetros que definen una operación simétrica:

a.f(b); <-> b.f(a);

Si sabe que b nunca puede ser nulo, simplemente puede intercambiarlo. Es más útil para iguales: en lugar de foo.equals("bar");hacerlo mejor"bar".equals(foo); .

Johannes Schaub - litb
fuente
2
Pero luego debe suponer equals(podría ser cualquier método) manejará nulo correctamente. Realmente todo lo que está haciendo es pasar la responsabilidad a otra persona (u otro método).
Supericy
44
@Supericy Básicamente sí, pero equals(o cualquier método) tiene que verificar de nulltodos modos. O declare explícitamente que no.
Angelo Fuchs
74

En lugar del patrón de objeto nulo, que tiene sus usos, puede considerar situaciones en las que el objeto nulo es un error.

Cuando se lanza la excepción, examine la traza de la pila y resuelva el error.

Jim Nelson
fuente
17
El problema es que generalmente pierde contexto, ya que NullPointerException no indica QUÉ variable era nula y puede tener varias operaciones "." En la línea. El uso de "if (foo == null) throw new RuntimeException (" foo == null ")" le permite indicar explícitamente LO que estaba mal, dando a su pila un valor mucho más valioso para aquellos que tienen que arreglarlo.
Thorbjørn Ravn Andersen
1
Con Andersen, me encantaría que el sistema de excepciones de Java pueda incluir el nombre de una variable en la que se está trabajando, de modo que NullPointerExceptions no solo indique la línea en la que ocurrió la excepción, sino también el nombre de la variable. Esto debería funcionar bien en software no ofuscado.
fwielstra
Todavía no he logrado que funcione, pero esto está destinado a resolver exactamente ese problema.
MatrixFrog
¿Cuál es el problema? Simplemente tome el NPE en el nivel apropiado donde haya suficiente contexto disponible, descargue la información de contexto y vuelva a lanzar la excepción ... es muy fácil con Java.
user1050755
3
Tenía un profesor que predicaba contra el método de encadenamiento de llamadas. Su teoría era que debes tener cuidado con las cadenas de llamadas que tenían más de 2 métodos. No sé si esa es una regla difícil, pero definitivamente elimina la mayoría de los problemas con los rastros de la pila NPE.
RustyTheBoyRobot
74

Nulo no es un "problema". Es una parte integral de un conjunto completo de herramientas de modelado. El software tiene como objetivo modelar la complejidad del mundo y el nulo lleva su carga. Nulo indica 'Sin datos' o 'Desconocido' en Java y similares. Por lo tanto, es apropiado utilizar valores nulos para estos fines. No prefiero el patrón 'Objeto nulo'; Creo que surge el problema de " quién protegerá a los guardianes ".
Si me preguntas cómo se llama mi novia, te diré que no tengo novia. En el lenguaje Java devolveré nulo. Una alternativa sería lanzar una excepción significativa para indicar algún problema que no puede ser (o no

  1. Para una 'pregunta desconocida', dé una 'respuesta desconocida'. (Sea nulo-seguro cuando esto sea correcto desde el punto de vista comercial) Verificar los argumentos para nulo una vez dentro de un método antes de su uso alivia a varias personas que llaman de verificarlos antes de una llamada.

    public Photo getPhotoOfThePerson(Person person) {
        if (person == null)
            return null;
        // Grabbing some resources or intensive calculation
        // using person object anyhow.
    }

    Anterior conduce a un flujo lógico normal para no obtener una foto de una novia inexistente de mi biblioteca de fotos.

    getPhotoOfThePerson(me.getGirlfriend())

    Y encaja con la nueva API Java que viene (mirando hacia adelante)

    getPhotoByName(me.getGirlfriend()?.getName())

    Si bien es más bien 'flujo comercial normal' no encontrar fotos almacenadas en la base de datos para alguna persona, solía usar pares como a continuación para otros casos

    public static MyEnum parseMyEnum(String value); // throws IllegalArgumentException
    public static MyEnum parseMyEnumOrNull(String value);

    Y no detesta escribir <alt> + <shift> + <j>(generar javadoc en Eclipse) y escribir tres palabras adicionales para su API pública. Esto será más que suficiente para todos, excepto para aquellos que no leen la documentación.

    /**
     * @return photo or null
     */

    o

    /**
     * @return photo, never null
     */
  2. Este es un caso bastante teórico y, en la mayoría de los casos, debe preferir la API segura nula de Java (en caso de que se lance en otros 10 años), pero NullPointerExceptiones una subclase de una Exception. Por lo tanto, es una forma de Throwableque indica las condiciones que una aplicación razonable puede querer atrapar ( javadoc ). Para usar la primera ventaja más de las excepciones y separar el código de manejo de errores del código 'regular' ( según los creadores de Java ) es apropiado, como para mí, atrapar NullPointerException.

    public Photo getGirlfriendPhoto() {
        try {
            return appContext.getPhotoDataSource().getPhotoByName(me.getGirlfriend().getName());
        } catch (NullPointerException e) {
            return null;
        }
    }

    Pueden surgir preguntas:

    P. ¿Qué pasa si getPhotoDataSource()devuelve nulo?
    A. Depende de la lógica de negocios. Si no encuentro un álbum de fotos, no te mostraré fotos. ¿Qué pasa si appContext no se inicializa? La lógica de negocios de este método lo soporta. Si la misma lógica debe ser más estricta, entonces lanzar una excepción es parte de la lógica de negocios y se debe usar la verificación explícita de nulo (caso 3). los nueva API Java Null-safe encaja mejor aquí para especificar selectivamente lo que implica y lo que no implica que se inicialice para fallar rápidamente en caso de errores del programador.

    P. Se podría ejecutar código redundante y se podrían obtener recursos innecesarios.
    R. Podría tener lugar si getPhotoByName()intentara abrir una conexión de base de datos, crear PreparedStatementy usar el nombre de la persona como un parámetro SQL por fin. El enfoque para una pregunta desconocida da una respuesta desconocida (caso 1) funciona aquí. Antes de obtener recursos, el método debe verificar los parámetros y devolver el resultado 'desconocido' si es necesario.

    P. Este enfoque tiene una penalización de rendimiento debido a la apertura de cierre de prueba.
    A. El software debe ser fácil de entender y modificar en primer lugar. Solo después de esto, uno podría pensar en el rendimiento, ¡y solo si es necesario! y donde sea necesario! ( fuente ) y muchos otros).

    PD. Este enfoque será tan razonable de usar como el código de manejo de errores separado del principio del código "regular" es razonable de usar en algún lugar. Considere el siguiente ejemplo:

    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {
        try {
            Result1 result1 = performSomeCalculation(predicate);
            Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
            Result3 result3 = performThirdCalculation(result2.getSomeProperty());
            Result4 result4 = performLastCalculation(result3.getSomeProperty());
            return result4.getSomeProperty();
        } catch (NullPointerException e) {
            return null;
        }
    }
    
    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {
        SomeValue result = null;
        if (predicate != null) {
            Result1 result1 = performSomeCalculation(predicate);
            if (result1 != null && result1.getSomeProperty() != null) {
                Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
                if (result2 != null && result2.getSomeProperty() != null) {
                    Result3 result3 = performThirdCalculation(result2.getSomeProperty());
                    if (result3 != null && result3.getSomeProperty() != null) {
                        Result4 result4 = performLastCalculation(result3.getSomeProperty());
                        if (result4 != null) {
                            result = result4.getSomeProperty();
                        }
                    }
                }
            }
        }
        return result;
    }

    PPS Para aquellos que votan negativamente (y no tan rápido para leer la documentación), me gustaría decir que nunca he visto una excepción de puntero nulo (NPE) en mi vida. Pero esta posibilidad fue diseñada intencionalmente por los creadores de Java porque NPE es una subclase de Exception. Tenemos un precedente en la historia de Java cuando ThreadDeathes un Errorno, ya que es en realidad un error de aplicación, sino únicamente porque no tenía la intención de ser atrapado! ¡Cuánto NPE cabe para ser un Errorque ThreadDeath! Pero no lo es.

  3. Verifique 'Sin datos' solo si la lógica de negocios lo implica.

    public void updatePersonPhoneNumber(Long personId, String phoneNumber) {
        if (personId == null)
            return;
        DataSource dataSource = appContext.getStuffDataSource();
        Person person = dataSource.getPersonById(personId);
        if (person != null) {
            person.setPhoneNumber(phoneNumber);
            dataSource.updatePerson(person);
        } else {
            Person = new Person(personId);
            person.setPhoneNumber(phoneNumber);
            dataSource.insertPerson(person);
        }
    }

    y

    public void updatePersonPhoneNumber(Long personId, String phoneNumber) {
        if (personId == null)
            return;
        DataSource dataSource = appContext.getStuffDataSource();
        Person person = dataSource.getPersonById(personId);
        if (person == null)
            throw new SomeReasonableUserException("What are you thinking about ???");
        person.setPhoneNumber(phoneNumber);
        dataSource.updatePerson(person);
    }

    Si appContext o dataSource no se inicializan en tiempo de ejecución no controlado, NullPointerException matará el subproceso actual y será procesado por Thread.defaultUncaughtExceptionHandler (para que defina y use su registrador favorito u otro mecanismo de notificación). Si no se establece, ThreadGroup # uncaughtException imprimirá stacktrace en el sistema err. Uno debe monitorear el registro de errores de la aplicación y abrir el problema de Jira para cada excepción no manejada que de hecho es un error de la aplicación. El programador debe corregir el error en algún lugar de las cosas de inicialización.

Mykhaylo Adamovych
fuente
66
Atrapar NullPointerExceptiony regresar nulles horrible de depurar. De todos modos, terminas con NPE más tarde, y es realmente difícil descubrir qué era originalmente nulo.
artbristol
23
Votaría en contra si tuviera la reputación. No solo no es necesario, es un agujero en el sistema de tipos. Asignar un árbol a una lista es un error de tipo porque los árboles no son valores de tipo Lista; por esa misma lógica, asignar nulo debería ser un error de tipo porque nulo no es un valor de tipo Objeto, o cualquier tipo útil para el caso. Incluso el hombre que inventó nulo lo considera su "error de mil millones de dólares". La noción de "un valor que podría ser un valor de tipo T O nada" es su propio tipo y debe representarse como tal (por ejemplo, Quizás <T> u Opcional <T>).
Doval
2
A partir de "Tal vez <T> u Opcional <T>" todavía necesita escribir código if (maybeNull.hasValue()) {...}así, ¿cuál es la diferencia if (maybeNull != null)) {...}?
Mykhaylo Adamovych 01 de
1
A partir de "atrapar NullPointerException y devolver nulo es horrible de depurar. De todos modos terminas con NPE más tarde, y es realmente difícil descubrir qué era originalmente nulo". ¡Estoy completamente de acuerdo! En esos casos, debe escribir una docena de declaraciones 'if' o arrojar NPE si la lógica de negocios implica datos en el lugar, o usar un operador nulo seguro de Java nuevo. Pero hay casos en los que no me importa qué paso exacto me da nulo. Por ejemplo, calcular algunos valores para el usuario justo antes de mostrar en la pantalla cuando se espera que falten datos.
Mykhaylo Adamovych
3
@MykhayloAdamovych: El beneficio de Maybe<T>o Optional<T>no está en el caso de que Tpueda ser nulo, sino en el caso de que nunca deba ser nulo. Si tiene un tipo que explícitamente significa "este valor puede ser nulo, úselo con precaución", y usa y devuelve dicho tipo de manera consistente, entonces cada vez que vea un viejo Ten su código, puede asumir que nunca es nulo. (Por supuesto, esto sería mucho más útil si el compilador lo
pudiera hacer cumplir
71

Java 7 tiene una nueva java.util.Objectsclase de utilidad en la que hay un requireNonNull()método. Todo lo que hace es arrojar un NullPointerExceptionsi su argumento es nulo, pero limpia un poco el código. Ejemplo:

Objects.requireNonNull(someObject);
someObject.doCalc();

El método es más útil para verificar justo antes de una asignación en un constructor, donde cada uso del mismo puede guardar tres líneas de código:

Parent(Child child) {
   if (child == null) {
      throw new NullPointerException("child");
   }
   this.child = child;
}

se convierte

Parent(Child child) {
   this.child = Objects.requireNonNull(child, "child");
}
Raedwald
fuente
99
En realidad, su ejemplo constituye una hinchazón de código: la primera línea es superflua porque el NPE se lanzaría en la segunda línea. ;-)
user1050755
1
Cierto. Un mejor ejemplo sería si la segunda línea fuera doCalc(someObject).
Stuart Marks
Depende Si usted es el autor de doCalc (), le sugiero que ponga el cheque en el cuerpo de ese método (si es posible). Y lo más probable es que llame a someObject.someMethod () donde nuevamente no hay necesidad de verificar si es nulo. :-)
user1050755
Bueno, si no eres el autor de doCalc(), y no arroja NPE de inmediato cuando se le da un valor nulo, deberías verificarlo y tirar NPE tú mismo. Para eso Objects.requireNonNull()es eso .
Stuart Marks el
77
No es solo código hinchado. Es mejor verificar por adelantado que a la mitad de un método que causa efectos secundarios o utiliza tiempo / espacio.
Rob Grant el
51

Finalmente, la única forma de resolver completamente este problema es usar un lenguaje de programación diferente:

  • En Objective-C, puede hacer el equivalente a invocar un método nil, y no pasará absolutamente nada. Esto hace que la mayoría de las verificaciones nulas sean innecesarias, pero puede hacer que los errores sean mucho más difíciles de diagnosticar.
  • En Niza , un lenguaje derivado de Java, hay dos versiones de todos los tipos: una versión potencialmente nula y una versión no nula. Solo puede invocar métodos en tipos no nulos. Los tipos potencialmente nulos se pueden convertir en tipos no nulos mediante la comprobación explícita de nulos. Esto hace que sea mucho más fácil saber dónde son necesarias las comprobaciones nulas y dónde no.
Michael Borgwardt
fuente
Woah ... La respuesta más correcta y su voto negativo, ¿dónde está la justicia? En Java, nulo siempre es un valor válido. Es el corolario de Todo es un Objeto: Nulo es un Todo (por supuesto, estamos ignorando las primitivas aquí, pero se entiende la idea). Personalmente, estoy a favor del enfoque adoptado por Nice, aunque podríamos hacerlo para que los métodos puedan invocarse en tipos anulables y promover NPE a excepciones comprobadas. Sin embargo, esto debería hacerse a través de un conmutador de compilación, ya que rompería todo el código existente :(
CurtainDog
44
No estoy familiarizado con Niza, pero Kotlin implementa la misma idea, tiene tipos anulables y no nulos integrados en el sistema de tipos del lenguaje. Mucho más conciso que el patrón de objeto opcional o nulo.
mtsahakis
38

"Problema" común en Java de hecho.

Primero, mis pensamientos sobre esto:

Considero que es malo "comer" algo cuando se pasó NULL donde NULL no es un valor válido. Si no está saliendo del método con algún tipo de error, significa que nada salió mal en su método, lo que no es cierto. Entonces, probablemente devuelva nulo en este caso, y en el método de recepción vuelva a verificar si es nulo, y nunca termina, y termina con "if! = Null", etc.

Entonces, en mi humilde opinión, nulo debe ser un error crítico que impide una mayor ejecución (es decir, donde nulo no es un valor válido).

La forma en que resuelvo este problema es la siguiente:

Primero, sigo esta convención:

  1. Todos los métodos públicos / API siempre verifican que sus argumentos sean nulos
  2. Todos los métodos privados no verifican si son nulos, ya que son métodos controlados (solo deje morir con excepción de puntero nulo en caso de que no se haya manejado anteriormente)
  3. Los únicos otros métodos que no comprueban nulo son los métodos de utilidad. Son públicos, pero si los llama por alguna razón, sabe qué parámetros pasa. Esto es como tratar de hervir agua en la tetera sin proporcionar agua ...

Y finalmente, en el código, la primera línea del método público es así:

ValidationUtils.getNullValidator().addParam(plans, "plans").addParam(persons, "persons").validate();

Tenga en cuenta que addParam () devuelve self, para que pueda agregar más parámetros para verificar.

El método validate()arrojará marcado ValidationExceptionsi alguno de los parámetros es nulo (marcado o desmarcado es más un problema de diseño / sabor, pero mi ValidationExceptionestá marcado).

void validate() throws ValidationException;

El mensaje contendrá el siguiente texto si, por ejemplo, "planes" es nulo:

" Se encontró un valor de argumento ilegal nulo para el parámetro [planes] "

Como puede ver, el segundo valor en el método addParam () (cadena) es necesario para el mensaje del usuario, porque no puede detectar fácilmente el nombre de la variable pasada, incluso con reflexión (de todos modos no es tema de esta publicación ...).

Y sí, sabemos que más allá de esta línea ya no encontraremos un valor nulo, por lo que simplemente invocamos métodos con seguridad en esos objetos.

De esta manera, el código es limpio, fácil de mantener y legible.

Oleg
fuente
3
Absolutamente. Las aplicaciones que simplemente arrojan el error y se bloquean son de mayor calidad porque no hay duda de que no están funcionando. En el mejor de los casos, las aplicaciones que se tragan los errores se degradan con gracia, pero por lo general no funcionan de manera difícil de notar y no se solucionan. Y cuando se nota el problema, son mucho más difíciles de depurar.
Paul Jackson
35

Hacer esa pregunta señala que puede estar interesado en las estrategias de manejo de errores. El arquitecto de su equipo debe decidir cómo trabajar los errores. Hay varias formas de hacerlo:

  1. permita que las Excepciones se propaguen: atrapelas en el 'bucle principal' o en alguna otra rutina de administración.

    • compruebe las condiciones de error y trátelas adecuadamente

Claro que también eche un vistazo a la Programación Orientada a Aspectos: tienen buenas formas de insertar if( o == null ) handleNull()en su código de bytes.

xtofl
fuente
35

Además de usar assert, puede usar lo siguiente:

if (someobject == null) {
    // Handle null here then move on.
}

Esto es un poco mejor que:

if (someobject != null) {
    .....
    .....



    .....
}
fastcodejava
fuente
2
Mh, ¿por qué eso? Por favor, no te sientas a la defensiva, solo me gustaría aprender más sobre Java :)
Matthias Meid
99
@Mudu Como regla general, prefiero que la expresión en una declaración if sea una declaración más "positiva", en lugar de una "negativa". Entonces, si lo viera, if (!something) { x(); } else { y(); }me inclinaría a refactorizarlo como if (something) { y(); } else { x(); }(aunque uno podría argumentar que != nulles la opción más positiva ...). Pero lo más importante, la parte importante del código no está envuelta dentro de {}sy tiene un nivel menos de sangría para la mayoría del método. No sé si ese fue el razonamiento de fastcodejava, pero sería mío.
MatrixFrog
1
Esto es lo que suelo hacer también. Mantiene el código limpio en mi opinión.
Koray Tugay
34

Simplemente no uses nulo. No lo permitas

En mis clases, la mayoría de los campos y las variables locales tienen valores predeterminados no nulos, y agrego declaraciones de contrato (afirmaciones siempre activadas) en todas partes del código para asegurarme de que esto se aplique (ya que es más sucinto y más expresivo que dejarlo) aparecer como un NPE y luego tener que resolver el número de línea, etc.).

Una vez que adopté esta práctica, noté que los problemas parecían solucionarse. Descubriría cosas mucho antes en el proceso de desarrollo solo por accidente y se daría cuenta de que tenía un punto débil ... y, lo que es más importante, ayuda a encapsular las preocupaciones de los diferentes módulos, los diferentes módulos pueden 'confiar' entre sí y no dejar más basura. código conif = null else construcciones!

Esta es una programación defensiva y da como resultado un código mucho más limpio a largo plazo. Siempre desinfecte los datos, por ejemplo, haciendo cumplir estándares rígidos, y los problemas desaparecerán.

class C {
    private final MyType mustBeSet;
    public C(MyType mything) {
       mustBeSet=Contract.notNull(mything);
    }
   private String name = "<unknown>";
   public void setName(String s) {
      name = Contract.notNull(s);
   }
}


class Contract {
    public static <T> T notNull(T t) { if (t == null) { throw new ContractException("argument must be non-null"); return t; }
}

Los contratos son como pruebas de mini unidades que siempre se ejecutan, incluso en producción, y cuando las cosas fallan, ya sabes por qué, en lugar de un NPE aleatorio, tienes que resolverlo de alguna manera.

iangreen
fuente
¿Por qué esto sería rechazado? en mi experiencia, esto es muy superior a los otros enfoques, me encantaría saber por qué no
ianpojman
Estoy de acuerdo, este enfoque previene los problemas relacionados con los nulos, en lugar de solucionarlos al marcar el código con nulos en todas partes.
ChrisBlom
77
El problema con este enfoque es que si el nombre nunca se establece, tiene el valor "<desconocido>", que se comporta como un valor establecido. Ahora supongamos que necesito verificar si el nombre nunca se configuró (desconocido), tengo que hacer una comparación de cadenas con el valor especial "<desconocido>".
Steve Kuo
1
Verdadero buen punto Steve. Lo que a menudo hago es tener ese valor como una constante, por ejemplo, public static final String UNSET = "__ unset" ... private String field = UNSET ... then private boolean isSet () {return UNSET.equals (field); }
ianpojman
En mi humilde opinión, esta es una implementación de patrón de objeto nulo con una implementación de usted mismo de opcional (contrato). ¿Cómo se comporta en la clase de clase de persistencia? No veo aplicable en ese caso.
Kurapika
31

Guava, una biblioteca central muy útil de Google, tiene una API agradable y útil para evitar nulos. Encuentro UsingAndAvoidingNullExplained muy útil.

Como se explica en la wiki:

Optional<T>es una forma de reemplazar una referencia T anulable con un valor no nulo. Un Opcional puede contener una referencia T no nula (en cuyo caso decimos que la referencia está "presente") o puede no contener nada (en cuyo caso decimos que la referencia está "ausente"). Nunca se dice que "contenga nulo".

Uso:

Optional<Integer> possible = Optional.of(5);
possible.isPresent(); // returns true
possible.get(); // returns 5
Murat
fuente
@CodyGuldner Derecha, Cody. Proporcioné una cita relevante del enlace para dar más contexto.
Murat Derya Özen
25

Este es un problema muy común para todos los desarrolladores de Java. Por lo tanto, hay soporte oficial en Java 8 para abordar estos problemas sin código desordenado.

Java 8 ha introducido java.util.Optional<T>. Es un contenedor que puede contener o no un valor no nulo. Java 8 ha dado una forma más segura de manejar un objeto cuyo valor puede ser nulo en algunos de los casos. Está inspirado en las ideas de Haskell y Scala .

En pocas palabras, la clase Opcional incluye métodos para tratar explícitamente los casos en los que un valor está presente o ausente. Sin embargo, la ventaja en comparación con las referencias nulas es que la clase Opcional <T> lo obliga a pensar en el caso cuando el valor no está presente. Como consecuencia, puede evitar excepciones de puntero nulo no intencionadas.

En el ejemplo anterior, tenemos una fábrica de servicios para el hogar que devuelve una manija a varios dispositivos disponibles en el hogar. Pero estos servicios pueden o no estar disponibles / funcionales; significa que puede dar lugar a una NullPointerException. En lugar de agregar un nuloif condición antes de usar cualquier servicio, envuélvala a Opcional <Servicio>.

ENVOLVER A LA OPCIÓN <T>

Consideremos un método para obtener una referencia de un servicio de una fábrica. En lugar de devolver la referencia del servicio, envuélvala con Opcional. Le permite al usuario de la API saber que el servicio devuelto puede o no estar disponible / funcional, usar a la defensiva

public Optional<Service> getRefrigertorControl() {
      Service s = new  RefrigeratorService();
       //...
      return Optional.ofNullable(s);
   }

Como puede ver, Optional.ofNullable()proporciona una manera fácil de envolver la referencia. Hay otras formas de obtener la referencia de Opcional, ya sea Optional.empty()&Optional.of() . Uno para devolver un objeto vacío en lugar de volver a sintonizar nulo y el otro para envolver un objeto no anulable, respectivamente.

Entonces, ¿cómo exactamente ayuda a evitar un cheque nulo?

Una vez que haya ajustado un objeto de referencia, Opcional proporciona muchos métodos útiles para invocar métodos en una referencia ajustada sin NPE.

Optional ref = homeServices.getRefrigertorControl();
ref.ifPresent(HomeServices::switchItOn);

Opcional. Si Presente invoca al Consumidor dado con una referencia si es un valor no nulo. De lo contrario, no hace nada.

@FunctionalInterface
public interface Consumer<T>

Representa una operación que acepta un único argumento de entrada y no devuelve ningún resultado. A diferencia de la mayoría de las otras interfaces funcionales, se espera que Consumer opere a través de efectos secundarios. Es muy limpio y fácil de entender. En el ejemplo de código anterior, HomeService.switchOn(Service)se invoca si la referencia de retención Opcional no es nula.

Usamos el operador ternario con mucha frecuencia para verificar la condición nula y devolver un valor alternativo o valor predeterminado. Opcional proporciona otra forma de manejar la misma condición sin marcar nulo. Opcional. O Else (defaultObj) devuelve defaultObj si el Opcional tiene un valor nulo. Usemos esto en nuestro código de muestra:

public static Optional<HomeServices> get() {
    service = Optional.of(service.orElse(new HomeServices()));
    return service;
}

Ahora HomeServices.get () hace lo mismo, pero de una mejor manera. Comprueba si el servicio ya está inicializado o no. Si es así, devuelva el mismo o cree un nuevo servicio nuevo. Opcional <T> .orElse (T) ayuda a devolver un valor predeterminado.

Finalmente, aquí está nuestro NPE, así como el código nulo sin cheques:

import java.util.Optional;
public class HomeServices {
    private static final int NOW = 0;
    private static Optional<HomeServices> service;

public static Optional<HomeServices> get() {
    service = Optional.of(service.orElse(new HomeServices()));
    return service;
}

public Optional<Service> getRefrigertorControl() {
    Service s = new  RefrigeratorService();
    //...
    return Optional.ofNullable(s);
}

public static void main(String[] args) {
    /* Get Home Services handle */
    Optional<HomeServices> homeServices = HomeServices.get();
    if(homeServices != null) {
        Optional<Service> refrigertorControl = homeServices.get().getRefrigertorControl();
        refrigertorControl.ifPresent(HomeServices::switchItOn);
    }
}

public static void switchItOn(Service s){
         //...
    }
}

La publicación completa es NPE, así como un código de verificación nulo ... ¿En serio? .

Yogesh Devatraj
fuente
Hay un cheque nulo en el código anterior, if(homeServices != null) {que se puede cambiar a homeServices.ifPresent(h -> //action);
KrishPrabakar
23

Me gustan los artículos de Nat Pryce. Aquí están los enlaces:

En los artículos también hay un enlace a un repositorio de Git para un Java Maybe Type que me parece interesante, pero no creo que por sí solo pueda disminuir la hinchazón del código de verificación. Después de investigar un poco en Internet, creo que ! = La expansión de código nulo podría reducirse principalmente mediante un diseño cuidadoso.

Mr Palo
fuente
Michael Feathers ha escrito un texto breve e interesante sobre enfoques como el que usted mencionó: manuelp.newsblur.com/site/424
ivan.aguirre
21

He intentado el NullObjectPatternpero para mí no siempre es el mejor camino a seguir. A veces hay una "no acción" que no es apropiada.

NullPointerExceptiones una excepción en tiempo de ejecución que significa que es culpa de los desarrolladores y con suficiente experiencia le dice exactamente dónde está el error.

Ahora a la respuesta:

Intente hacer que todos sus atributos y sus accesores sean lo más privados posible o evite exponerlos a los clientes. Puede tener los valores de argumento en el constructor, por supuesto, pero al reducir el alcance no permite que la clase del cliente pase un valor no válido. Si necesita modificar los valores, siempre puede crear uno nuevo object. Comprueba los valores en el constructor solo una vez y en el resto de los métodos puede estar casi seguro de que los valores no son nulos.

Por supuesto, la experiencia es la mejor manera de comprender y aplicar esta sugerencia.

¡Byte!

OscarRyz
fuente
19

Probablemente la mejor alternativa para Java 8 o posterior es usar la Optionalclase.

Optional stringToUse = Optional.of("optional is there");
stringToUse.ifPresent(System.out::println);

Esto es especialmente útil para largas cadenas de posibles valores nulos. Ejemplo:

Optional<Integer> i = Optional.ofNullable(wsObject.getFoo())
    .map(f -> f.getBar())
    .map(b -> b.getBaz())
    .map(b -> b.getInt());

Ejemplo sobre cómo lanzar una excepción en nulo:

Optional optionalCarNull = Optional.ofNullable(someNull);
optionalCarNull.orElseThrow(IllegalStateException::new);

Java 7 introdujo el Objects.requireNonNullmétodo que puede ser útil cuando se debe verificar que algo no sea nulo. Ejemplo:

String lowerVal = Objects.requireNonNull(someVar, "input cannot be null or empty").toLowerCase();
Raghu K Nair
fuente
17

¡Puedo responderlo de manera más general!

Nosotros usualmente enfrentamos a este problema cuando los métodos obtienen los parámetros de la manera que no esperábamos (la llamada al método incorrecta es culpa del programador). Por ejemplo: espera obtener un objeto, en su lugar, obtiene un valor nulo. Esperas obtener una Cadena con al menos un carácter, en su lugar obtienes una Cadena vacía ...

Entonces no hay diferencia entre:

if(object == null){
   //you called my method badly!

}

o

if(str.length() == 0){
   //you called my method badly again!
}

Ambos quieren asegurarse de que hayamos recibido parámetros válidos, antes de realizar cualquier otra función.

Como se menciona en algunas otras respuestas, para evitar los problemas anteriores, puede seguir el patrón Diseño por contrato . Consulte http://en.wikipedia.org/wiki/Design_by_contract .

Para implementar este patrón en java, puede usar anotaciones centrales de java como javax.annotation.NotNull o usar bibliotecas más sofisticadas como Hibernate Validator .

Solo una muestra:

getCustomerAccounts(@NotEmpty String customerId,@Size(min = 1) String accountType)

Ahora puede desarrollar de forma segura la función principal de su método sin necesidad de verificar los parámetros de entrada, protegen sus métodos de parámetros inesperados.

Puede ir un paso más allá y asegurarse de que solo se puedan crear pojos válidos en su aplicación. (muestra del sitio de validación de hibernación)

public class Car {

   @NotNull
   private String manufacturer;

   @NotNull
   @Size(min = 2, max = 14)
   private String licensePlate;

   @Min(2)
   private int seatCount;

   // ...
}
Alireza Fattahi
fuente
javaxes, por definición, no "núcleo de Java".
Tomás
17

No hago caso de las respuestas que sugieren usar los objetos nulos en cada situación. Este patrón puede romper el contrato y enterrar los problemas cada vez más en vez de resolverlos, sin mencionar que si se usa de manera inapropiada, se creará otro montón de código repetitivo que requerirá mantenimiento futuro.

En realidad, si algo devuelto por un método puede ser nulo y el código de llamada tiene que tomar una decisión al respecto, debería haber una llamada anterior que garantice el estado.

También tenga en cuenta que ese patrón de objeto nulo requerirá memoria si se usa sin cuidado. Para esto, la instancia de un NullObject debe compartirse entre los propietarios y no ser una instancia única para cada uno de ellos.

Además, no recomendaría usar este patrón donde el tipo está destinado a ser una representación de tipo primitiva, como entidades matemáticas, que no son escalares: vectores, matrices, números complejos y objetos POD (datos antiguos simples), que están destinados a mantener el estado en forma de tipos incorporados de Java. En el último caso, terminaría llamando a métodos getter con resultados arbitrarios. Por ejemplo, ¿qué debería devolver un método NullPerson.getName ()?

Vale la pena considerar tales casos para evitar resultados absurdos.

luke1985
fuente
La solución con "hasBackground ()" tiene un inconveniente: no es seguro para subprocesos. Si necesita llamar a dos métodos en lugar de uno, debe sincronizar toda la secuencia en un entorno multiproceso.
pkalinow
@pkalinow Has hecho un ejemplo artificial solo para señalar que esta solución tiene un inconveniente. Si el código no está destinado a ejecutarse en una aplicación multiproceso, entonces no hay inconveniente. Podría ponerle probablemente el 90% de su código que no es seguro para subprocesos. No estamos hablando aquí sobre este aspecto del código, estamos hablando del patrón de diseño. Y el subprocesamiento múltiple es un tema en sí mismo.
luke1985
Por supuesto, en una aplicación de un solo hilo no es un problema. He dado ese comentario porque a veces es un problema.
pkalinow
@pkalinow Si estudia este tema más de cerca, descubrirá que el patrón de diseño de objeto nulo no solucionará los problemas de subprocesos múltiples. Entonces es irrelevante. Y para ser honesto, he encontrado lugares donde este patrón encajaría muy bien, por lo que mi respuesta original es un poco incorrecta, en realidad.
luke1985
16
  1. Nunca inicialice las variables a nulo.
  2. Si (1) no es posible, inicialice todas las colecciones y matrices para vaciar colecciones / matrices.

Haciendo esto en su propio código y puede evitar! = Comprobaciones nulas.

La mayoría de las veces, las comprobaciones nulas parecen proteger los bucles sobre colecciones o matrices, así que simplemente inicialícelas vacías, no necesitará ninguna comprobación nula.

// Bad
ArrayList<String> lemmings;
String[] names;

void checkLemmings() {
    if (lemmings != null) for(lemming: lemmings) {
        // do something
    }
}



// Good
ArrayList<String> lemmings = new ArrayList<String>();
String[] names = {};

void checkLemmings() {
    for(lemming: lemmings) {
        // do something
    }
}

Hay una pequeña sobrecarga en esto, pero vale la pena por un código más limpio y menos NullPointerExceptions.

Stuart Axon
fuente
3
+1 Con esto estoy de acuerdo. Nunca debe devolver objetos medio inicializados. El código relacionado con Jaxb y el código de bean es notiroius para esto. Es una mala practica. Todas las colecciones deben inicializarse y todos los objetos deben existir (idealmente) sin referencias nulas. Considere un objeto que tiene una colección. Comprobar que el objeto no es nulo, que la colección no es nula y que la colección no contiene objetos nulos es irrazonable y tonto.
ggb667
15

Este es el error más común ocurrido para la mayoría de los desarrolladores.

Tenemos varias formas de manejar esto.

Enfoque 1:

org.apache.commons.lang.Validate //using apache framework

notNull (objeto objeto, mensaje de cadena)

Enfoque 2:

if(someObject!=null){ // simply checking against null
}

Enfoque 3:

@isNull @Nullable  // using annotation based validation

Enfoque 4:

// by writing static method and calling it across whereever we needed to check the validation

static <T> T isNull(someObject e){  
   if(e == null){
      throw new NullPointerException();
   }
   return e;
}
Sireesh Yarlagadda
fuente
Anuncio. 4. No es muy útil: cuando verifica si un puntero es nulo, probablemente desee llamar a un método. Llamar a un método en nulo le da el mismo comportamiento: NullPointerException.
pkalinow
11
public static <T> T ifNull(T toCheck, T ifNull) {
    if (toCheck == null) {
           return ifNull;
    }
    return toCheck;
}
tltester
fuente
2
Lo que está mal con este método, creo que @tltester solo quiere dar un valor predeterminado si es nulo, lo cual tiene sentido.
Sawyer
1
Existe un método tal en Apache commons-lang: ObjectUtils.defaultIfNull(). Hay otra más general: ObjectUtils.firstNonNull()que puede ser usado para implementar una estrategia degradantes:firstNonNull(bestChoice, secondBest, thirdBest, fallBack);
ivant