El uso de las requireNonNull()
primeras declaraciones en un método permite identificar ahora / rápidamente la causa de la excepción.
El seguimiento de la pila indica claramente que la excepción se produjo tan pronto como se introdujo el método porque la persona que llamó no respetó los requisitos / contrato.
Pasar un null
objeto a otro método puede provocar una excepción a la vez, pero la causa del problema puede ser más complicada de entender ya que la excepción se lanzará en una invocación específica sobre el null
objeto que puede estar mucho más lejos.
Aquí hay un ejemplo concreto y real que muestra por qué tenemos que favorecer el fracaso rápido en general y más particularmente el uso Object.requireNonNull()
o cualquier forma de realizar una verificación no nula en los parámetros diseñados para no serlo null
.
Supongamos que una Dictionary
clase que compone una LookupService
y una List
de String
representación de palabras contenidas en. Estos campos están diseñados para ser no null
y uno de ellos se pasa en el Dictionary
constructor.
Ahora suponga una implementación "incorrecta" Dictionary
sin null
verificación en la entrada del método (aquí está el constructor):
public class Dictionary {
private final List<String> words;
private final LookupService lookupService;
public Dictionary(List<String> words) {
this.words = this.words;
this.lookupService = new LookupService(words);
}
public boolean isFirstElement(String userData) {
return lookupService.isFirstElement(userData);
}
}
public class LookupService {
List<String> words;
public LookupService(List<String> words) {
this.words = words;
}
public boolean isFirstElement(String userData) {
return words.get(0).contains(userData);
}
}
Ahora, invoquemos al Dictionary
constructor con una null
referencia para el words
parámetro:
Dictionary dictionary = new Dictionary(null);
// exception thrown lately : only in the next statement
boolean isFirstElement = dictionary.isFirstElement("anyThing");
La JVM arroja el NPE a esta declaración:
return words.get(0).contains(userData);
Excepción en el hilo "main" java.lang.NullPointerException
en LookupService.isFirstElement (LookupService.java:5)
en Dictionary.isFirstElement (Dictionary.java:15)
en Dictionary.main (Dictionary.java:22)
La excepción se desencadena en la LookupService
clase mientras que su origen es bastante anterior (el Dictionary
constructor). Hace que el análisis general del problema sea mucho menos obvio.
Es words
null
? Es words.get(0) null
? Ambos ? ¿Por qué el uno, el otro o tal vez ambos son null
? ¿Es un error de codificación en Dictionary
(constructor? Método invocado?)? ¿Es un error de codificación LookupService
? (¿constructor? ¿método invocado?)?
Finalmente, tendremos que inspeccionar más código para encontrar el origen del error y, en una clase más compleja, tal vez incluso usar un depurador para comprender más fácilmente lo que sucedió.
Pero, ¿por qué una cosa simple (la falta de verificación nula) se convierte en un problema complejo?
Debido a que permitimos el error / falta inicial identificable en una fuga de componente específico en componentes inferiores.
Imagine que LookupService
no se trata de un servicio local, sino de un servicio remoto o de una biblioteca de terceros con poca información de depuración o imagine que no tenía 2 capas, sino 4 o 5 capas de invocaciones de objetos antes de que null
se detectara. El problema sería aún más complejo de analizar.
Entonces la forma de favorecer es:
public Dictionary(List<String> words) {
this.words = Objects.requireNonNull(words);
this.lookupService = new LookupService(words);
}
De esta manera, no hay dolor de cabeza: recibimos la excepción tan pronto como se recibe:
// exception thrown early : in the constructor
Dictionary dictionary = new Dictionary(null);
// we never arrive here
boolean isFirstElement = dictionary.isFirstElement("anyThing");
Excepción en el hilo "main" java.lang.NullPointerException
en java.util.Objects.requireNonNull (Objects.java:203)
en com.Dictionary. (Dictionary.java:15)
en com.Dictionary.main (Dictionary.java:24)
Tenga en cuenta que aquí ilustré el problema con un constructor, pero una invocación de método podría tener la misma restricción de comprobación no nula.