Los que conocemos las bondades de Java tenemos una relación más que especial con "NullPointerException", esa excepción que se arroja cuando se requería un objeto no nulo y, para sorpresa de todos, resulta que venía algo nulo. Sacado del javadoc estos son los casos en los cuáles se provoca:

Thrown when an application attempts to use null in a case where an object is required. These include:

  • Calling the instance method of a null object.
  • Accessing or modifying the field of a null object.
  • Taking the length of null as if it were an array.
  • Accessing or modifying the slots of null as if it were an array.
  • Throwing null as if it were a Throwable value.

El sentimiento que me produce cuando veo esa palabra en la pantalla es similar a cuando hacía prácticas en la universidad, en sistemas operativos, y me salía un error con segmentation fault o broken pipe. Me deja frío porque no me aporta nada, aparte del mosqueo que "lo que sea" no funcione bien. Creo que no hay excepción más poco útil que esta y, por desgracia, una de las más habituales.

La reacción normal es: "se me habrá olvidado inicializar algo" ... vale, se te ha pasado instanciar una lista o un mapa... puede empeorar, lo más normal es que necesites usar el modo depuración para comprobar los valores de las variables que entran en juego hasta que pilles a la "null" ... no, amigo, esto es el cazador cazado... Creo que es una de las peores impresiones que puede dar un software, que arroje una excepción "NullPointerException".

Por supuesto, hay gente que se ha molestado en pensar como solventar esto y cuáles son las buenas prácticas a seguir para asegurar que lo que "das" no sea nulo. Si llega un objeto nulo a un método, desarrollado por otra persona, y accede a su contenido (por ejemplo, llama un método), se invoca al diablo y será culpa tuya, no suya por no controlarlo. Por eso digo "das", es quién aporta la información quién debe asegurarse de lo que transmite. Nulo caca, vacío o valor por defecto guay! :)

¿Cuál es una forma válida de solucionar "la excepción de la muerte"? Bien, aquí paso a comentar brevemente algo sobre el patrón NullObject y la Lazy-Initialization. Ambos conceptos vienen a cubrir algo similar, el no retornar un objeto nulo, sin posibilidad que no se haya inicializado y no tenga "cuerpo" produciendo excepciones en cuanto se acceda a cualquier de sus partes. Además, la inicialización perezosa, tiene un toque de optimización ya que promueve inicializar un objeto solo cuando realmente sea necesario.

Veamos algo de código:

public Service getService() { 
     if (service == null)
         service = new MyServiceImpl(...); // Good enough default for most cases? 
     return service;
 }
 

Este fragmento lo he sacado del Clean Code, página 185, donde Uncle Bob nos habla de Lazy-initialization. Y no hay mucha más complejidad. La idea es simple, cuando vayamos a crear un método que, al invocarse, retorne información nos aseguramos que, al menos, no tendrás problemas con "null". Además no sobrecarga el sistema con la construcción del objeto a no ser que realmente se vaya a usar. Es muy posible que ese código os recuerde a cosas como el patrón singleton, factorias e inyección de dependencias. En caso contrario pincha y léete todos los enlaces que acabo de poner ;)

La aproximación de NullObject se centra sobre todo en no permitir retornar un valor nulo, no mira si ya existe o cosas así. También hay un libro del Tío Bob donde habla de él (libro que todavía no es de mi propiedad, todavía...) y el señor Fowler también lo comenta en Refactoring como se indica en este enlace. Un NullObject no es más que un objeto con cuerpo pero que no hace nada. Como antes, código ven a mi:

public interface animal {
 	public void makeSound();
 }
  
 public class dog implements animal {
 	public void makeSound() {
 		System.out.println("Guau!");
 	}
 }
  
 public class NullAnimal implements animal {
 	public void makeSound() { }
 }
 

He adaptado un fragmento que había en la wikipedia para mostrarlo en Java. Teniendo la posibilidad de instanciar objetos de este tipo nos quitamos los problemas de los  NullPointerException. Además, los que nos pegamos con el testing, ¿qué es sino un objeto "fake"? Es un objeto trucado. Esto es igual. De esta forma se puede inyectar para evitar la referencia nula.

No quiero alargarme más. Como siempre hay ya gente que ha hablado de esto y bien por la red, aquí os recopilo algunos enlaces de ambas aproximaciones: