86

Tengo mi programa de Java y me sale un NullPointerException y he visto otras preguntas pero son de gente con otros programas y no me sirve para mi programa y quiero dejaros aquí las 2.000 líneas de mi programa para que me solucionéis el problema pero la gente no me deja.

Mirad, mi programa es como esto

  Object a = null;
  // 500 líneas de código
  // Dentro de un bucle, dentro de un if, dentro de un for...
  a = algunaFuncionDeMilLineasQueNoTeVoyAMostrar(
     parámetrosDeLosQueNoTeDiréElValor)
  // Cerrando for, cerrando if, cerrando bucle.
  // Otras 500 líneas de código
  // Una de estas líneas lanza el NPE, pero no te diré cuál.
  a.toString();
  z.hazAlgunaCosa();
  x.haxY().hazM().cuantoFaltaParaLasVacaciones().andaMiraUnPatoQuePasaPorAhi();

¿Qué está mal? ¿Por qué no hay una solución que sirva para todos los NullPointerException (NPE) si se dan tan a menudo?

sstan
  • 16,591
  • 2
  • 21
  • 45
SJuan76
  • 10,771
  • 5
  • 17
  • 31
  • 2
    Inicializas todos los posibles valores nullos y listo. – Oscar May 20 '18 at 03:23
  • En mi corta experiencia he descubierto que la solución a todos los problemas de java es saber ejecutar un debug con cualquier IDE que estés utilizando sea Eclipse, Netbeans , Android Studio etc... – Jhon Jimenez Jun 09 '18 at 23:23
  • 2
    Si en tu caso te llega un NPE es porque no pudiste predecir un caso en tu programación lo cual es normal pero debes tener en cuenta que eres un mortal y cometerás errores, incluso sino lo hicieras el programa puede sufrir cambios inesperados por cuestiones externas como cambios en tu red de trabajo, entonces pon puntos de depuración y trata de seguir el paso a tu programa y así entenderás como esta funcionando y no te sentirás frustrado con respuestas inesperadas. – Jhon Jimenez Jun 09 '18 at 23:23
  • Dejar de usar Java :/ con esta idea en mente construyeron un nuevo lenguaje de programación [Rust](https://www.rust-lang.org/), y es bastante popular. – Oscar David Arbeláez Oct 13 '19 at 05:15
  • 1
    @OscarDavidArbeláez. Si tiene estos problemas con Java, no le recomendaria Rust. – Candid Moe Oct 03 '20 at 16:47

8 Answers8

113

La solución es (redoble de tambores)...

No usar métodos o propiedades de una variable o expresión que vale null

Si tienes una línea:

p.hacerAlgo();

y te lanza un NPE, es que al ejecutarla p vale null. Asegúrate de que tenga un valor no null antes de ejecutar la línea.

Eso es todo...

No hace falta repetir la misma pregunta un millón de veces, aunque el programa sea distinto.

Está claro, ¿no? Como prometía, resuelto para siempre jamás.

Si tu línea es algo así como

p.obtenerObjeto1().obtenerObjeto2().obtenerObjeto3().hacerAlgo();

es exactamente igual, si alguno de los métodos te devuelve null, al intentar llamar a su correspondiente método el JVM lanzará el NullPointerException. La única diferencia con el ejemplo anterior es que el JVM solo te mostrará error en la línea y tendrás que averiguar tú en qué paso aparece el null.

Lo anterior cambió un poco a partir de la versión 14 del lenguaje, la cual introdujo una opción para indicarle a la JVM que nos brinde mensajes más detallados sobre el código que lanza la excepción1. Esta opción está deshabilitada por defecto por lo que para habilitarla es necesario incluir el parámetro de línea de comandos de la JVM -XX:+ShowCodeDetailsInExceptionMessages al momento de ejecutar el programa.

$ java -XX:+ShowCodeDetailsInExceptionMessages ...

¿Y ahora es cuando alguien protesta: "Pero eso no me ayuda con mi problema de que me sale un NullPointerException en mi programa que aquí te escribo2"?

La respuesta: Si hay un NullPointerException, tu problema no es el NullPointerException.

Es responsabilidad de un programador saber cómo funciona su programa y cómo se organizan los datos. No es el tipo de pregunta que se pueda descargar a StackOverflow. Si tienes un NPE, estás con uno (o más) de estos casos:

  • Estás liado por la lógica del programa y no sigue el path que tú pensabas. Solución: Encárgate de depurar tu programa

El caso típico es decir "en este método a puede ser 1 ó 2" y hacer

    Object myObj = null;
    switch (a) {
    case 1:
      myOjb = new String("Hola");
      break;
    case 2:
      myObj = new Integer(1);
    }
    myObj.toString();

Tu problema es que a vale algo que no esperabas, unido a que no programas defensivamente y no controlas los valores. Depura tu programa para solucionar el valor de a, añade un default que lance una excepción cuando el valor sea inesperado.

  • Tienes un método tan complicado que no sabes cuándo se asignan valores a la referencia. Modifica tu programa para que sea fácil de leer y entender. No hagas como los antiguos elbonios que decían "Escribir es fácil, esperamos aprender a leer algún día". Si quieres comprobar si tu programa funciona cómo esperas, usa una herramienta de depuración o genera mensajes de log para que puedes verificar su funcionamiento. Si necesitas a alguien de Internet para que te explique como funciona tu programa, plantéate otra profesión.

  • Tienes un problema con el API. Has seguido todos los pasos arriba comentados, y al final has encontrado que la causa del origen es que estás llamando a java.util.Collections.toArray() y te devuelve un valor null. Revisa la documentación del método, revisa los parámetros que pasas, etc. Si sigues sin encontrar la solución, ¡¡¡¡Felicidades!!!! Ahora ya tienes una pregunta que es válida para SO ("¿Por qué java.util.Collections.toArray() me devuelve null?")

  • Tienes un vector que inicializaste de alguna forma, pero nunca lo inicializaste internamente. Hacer esto tipo1 vector[] = new tipo1[n]; no implica que el vector se llenará mágicamente de objetos de tipo1. Debes hacer un New tipo1 para cada posición del vector.

Una nota más sobre arrays

Como es una confusión que aparece con bastante frecuencia, insistiremos en el último punto:

 String miArray[] = new String[10];
 System.out.println(miArray.length); // Funciona
 System.out.println(miArray[0].isEmpty()); // NullPointerException

Al crear un array de objetos, se crea el array pero no se crean los objetos. Después de la primera línea del código de arriba, tienes el objeto miArray (puedes hacer miArray.length), pero ese objeto solo contiene 10 referencias (miArray[0], miArray[1]...) a null; las instancias a las que apuntan todavía se tienen que asignar. Si no se asigna una instancia a esas referencias, al intentar invocar métodos o atributos se lanzará el NullPointerException.


1Para más información consulte la JEP 358: Helpful NullPointerExceptions.

2Seguido de un millar de líneas de código, y por supuesto sin ni siquiera indicar en qué línea se lanza el NPE.

padaleiana
  • 2,175
  • 5
  • 16
  • 24
SJuan76
  • 10,771
  • 5
  • 17
  • 31
  • 4
    Excelente respuesta. Sería bueno agregar que el stacktrace indice exactamente la clase, el método y la línea de código donde saltó el NPE. –  Jan 08 '17 at 19:02
  • Una NPE se puede producir también aunque nunca "uses métodos o propiedades de una variable o expresión que vale null". Con `throw new NullPointerException("Porque soy un pedante. Mira otro pato.");` . Pero por lo demás perfecto, +1. – Anonymous Coward Jan 08 '17 at 21:55
19

Si sospechas que la variable trae un null y dará un error NullPointerException resuélvelo agregando un bloque try-catch y recuperá el error. Puedes dar a tus objetos un new Object y subirá el null como cadena a tu aplicativo y así puedes ver qué objeto es el que rompe el flujo de tu programa.

También puedes ir leyendo las advertencias en el código, ayudan mucho.

Martha Sanchez
  • 199
  • 1
  • 5
  • 3
    Hija @Martha. Bienvenida a [es.so] y gracias por tu aporte. Creo que en tu respuesta se podría complementar con un ejemplo de código y, quizás, algún enlace a la referencia sobre lo que estás comentando. La idea del sitio es generar preguntas y respuestas con la mayor calidad posible. Y, como un sitio de programación, muchas veces el código ayuda a contextualizar de qué se está hablando. No es necesario que las respuestas tengan código, pero muchas veces aumenta la calidad de una respuesta. Siempre se está a tiempo de [edit] – Mariano May 22 '17 at 22:27
  • 4
    Hola Martha. En general, la práctica aconsejada es evitar usar excepciones para control de flujo; en tu ejemplo un simple `if` sería a) más fácil de leer y entender b) probablemente mucho más rápido y c) menos sensible a errores (p.ej., si tienes `try { lista.get(0).hacer();} catch (NullPointerException npe) {`, en el `catch` no sabes si el `null` está en `lista`, en `lista.get(0)`, o viene de ejecutar `hacer()`. Más información (inglés): http://howtodoinjava.com/best-practices/java-exception-handling-best-practices/#14 y https://softwareengineering.stackexchange.com/questions/189222/ – SJuan76 May 23 '17 at 00:08
16

Que tal buenas tardes, claro que existe una solución al manejo del NPE y no es necesariamente el manejo de la excepción, te recomiendo que utilices el patrón de diseño Null Object Pattern , esto hará que tu código sea mas robusto y tendrás el control de este tipo de errores evitando así que programa se vea interrumpido en tiempo de ejecución.

Te dejo el diagrama UML:

UML Null Object Pattern

La implementación es realmente simple. te recomiendo que no hagas validaciones object.algo.algo != null y tampoco abuses de los try/catch generalizados por que eso no va solucionar tu problema solo lo estarán controlando pero el problema de fondo seguirá existiendo.

Saludos!!!

J VAZQUEZ V
  • 161
  • 1
  • 2
  • 8
    Hola. gracias por la respuesta. mira [answer]. podrias explicar como se implementa este patron? no todos saben uml. – gbianchi Nov 04 '17 at 00:36
12

Parte de tu problema seguramente sea lo de la funciondeMilLineas estas haciendo funciones muy largas y muy enrevesadas en las que es fácil equivocarse. Una regla general sencilla sobre lo larga que debe ser una función es que se pueda ver en una pantalla. Eso es entorno a unas 40 líneas en mi monitor. Se puede jugar con el tamaño de la letra, pero más o menos esa es la longitud.

No es sencillo aprender a dividir el código en funciones. Lo que he leído sobre el tema que más gustado ha sido en un libro de Robert C. Martin que se llama Código Limpio y tiene una capítulo entero sobre las funciones. Allí lo que dice es:

Una función debería hacer solo una cosa

Evidentemente cuesta un poco entender que significa esto y aplicarlo en la práctica. Pero yo creo que vale aprender eso antes que hacer programas que funcionen pero luego sean demasiado enrevesados para extenderlos.

Lo del NullPointerException es que no has incializado alguna referencia de Java. Simplificándolo a la bestia es que te falta algún new en algún sitio.

PD: Si de verdad llegas a una función de mil líneas no dudes que está mal. Eso hay que dividirlo.

jpuriol
  • 353
  • 1
  • 3
  • 10
  • Lo que comentas es bueno, pero lastimosamente a veces debemos mantener código legado, donde aplicar estas prácticas a veces es imposible por la complejidad misma del código. Pero es bueno tener presente el código limpio para proyectos nuevos o nuevas partes del código. –  Jan 08 '17 at 20:49
  • Una buena forma de reconocer si mi _funcionDe1000Lineas_ se puede dividir en otra _funcionDe500Lineas_ es preguntarte si todas las lineas de codigo corresponden con el *buen nombre que le asignaste*, si hay lineas que no calzan con el nombre, entonces es muy buena idea crearle una función solo para ellas. – NBPalomino Feb 16 '18 at 22:33
11

Si pudiera aportar algo, que no creo, porque han respondido bastante bien arriba (ahora daré el voto) añadiría:

-si es un parámetro, debes verificarlo antes de usarlo. -si es un resultado, usa opcionales

Ejemplo:

public Optional<String> unMetodoCualquiera( String parametro ) {
  Validate.notNull(parametro);
  ...
  return Optional.ofNullable(...);
}
earroyoron
  • 308
  • 2
  • 9
  • yo añadiría que hay que tener un poco de cuidado con Optional, ya que no es una mónoda propiamente y puede aún dar NPE si no se usa bien – Ruslan López Sep 19 '21 at 04:32
8

puedes implementar un try{}catch(NullPointerException){} ya que como bien dice atrapa todos los campos que son nullos o bien asegurate de que no sean nulos a la hora de llamarlos. puedes implementar un método que reciba el objeto y verificar que no sea nullo

public boolean isnull(Object a){

if(a==null){
 return true;
}
 return false
}

o bien mejor asegurate a la hora de programar que esté ya contenga la referencia o que este inicializado.

petronaMX
  • 69
  • 11
2

Hay una opción que no parece ser muy utilizada para mitigar el problema de las NullPointerExceptions, y es la clase java.util.Objects. Esta clase, presente en el JDK desde Java 7, contiene varios métodos estáticos que pueden ayudar bastante al manejo de nulls.

Lo más básico es simplemente testear si una variable es null o no. Usualmente se hace:

if (variable == null) 
// o
if (variable != null) 

A mi siempre me molestó un poco tener que usar == porque no suele ser la manera correcta de comparar objetos (aunque en este caso funciona como se espera). Con la clase objects podemos hacer:

if (Objects.isNull(variable))
// o
if (Objects.nonNull(variable))

Si además importamos Objects en forma estática:

if (isNull(variable))
// o
if (nonNull(variable))

Queda mucho más claro, conciso y elegante. Además, es mucho mejor aún cuando se usa en Streams, como por ejemplo en un filter:

filter(Objects::isNull)
// en lugar de:
filter(o -> o == null)

Otro método que me parece genial de la clase Objects, es requireNonNullElse() (aunque este método solo está presente desde la versión Java 9). Normalmente haríamos algo así si queremos asignar una variable verificando que no sea null:

if (variable == null) {
   otraVariable = "algún valor por omisión";
} else {
   otraVariable = variable;
}

(también podríamos usar el operador ternario aquí, pero sería similar). Con Objects, podríamos hacer:

otraVariable = requireNonNullElse(variable, "algún valor por omisión");

Hay otra versión similar que acepta un supplier como segundo argumento:

otraVariable = requireNonNullElseGet(variable, () -> new ObjectoComplicadoDeCrear());

Cuál es la diferencia entre los dos? por la manera en que Java funciona, requireNonNullElse() evaluará el segundo argumento aunque el primer argumento no sea null y no lo necesite retornar. Entonces si éste segundo argumento es algo que sea complicado de crear, que ocupe mucha memoria o mucho proceso, se puede utilizar requireNonNullElseGet() y construir el objecto en el cuerpo del supplier. Como el resultado de la expresión lambda no se va a evaluar hasta que realmente se la necesite, nos ahorramos de crear algo que no se va a utilizar.

Hay algunos otros métodos en la clase Objects que ayudan a prevenir NullPointerExceptions, pero estos me parecen los más importantes. Pueden consultar la documentación aquí: https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/util/Objects.html

Alex
  • 1,137
  • 4
  • 9
0

Se soluciona facil, validando. Se dice que los nullPointerException son generados por errores del programador, si vos sabes que un metodo puede devolver un objeto o necesita trabajar sobre un objeto solo coloca un:

if(objeto != null)
{
   // BLOQUE A EJECUTAR
}
Franco Torres
  • 562
  • 2
  • 12