validar - variable igual a null en java
Java: evitar la comprobaciĆ³n de nulos en clases anidadas(comprobaciĆ³n de nulos profundos) (8)
Imagina que tengo una clase familiar. Contiene una lista de personas. Cada persona (clase) contiene una dirección (clase). Cada dirección (clase) contiene un código postal (clase). Cualquier clase "intermedia" puede ser nula.
Entonces, ¿hay una forma sencilla de llegar a PostalCode sin tener que verificar el valor nulo en cada paso? es decir, ¿hay una manera de evitar el siguiente código de encadenamiento de margarita? Sé que no hay una solución Java "nativa", pero esperaba que alguien conozca una biblioteca o algo así. (marcó Commons y Guayaba y no vi nada)
if(family != null) {
if(family.getPeople() != null) {
if(family.people.get(0) != null) {
if(people.get(0).getAddress() != null) {
if(people.get(0).getAddress().getPostalCode() != null) {
//FINALLY MADE IT TO DO SOMETHING!!!
}
}
}
}
}
No, no se puede cambiar la estructura. Es de un servicio sobre el que no tengo control.
No, no puedo usar Groovy y es práctico el operador "Elvis".
No, prefiero no esperar a Java 8: D
No puedo creer que sea el primer desarrollador en cansarme de escribir un código como este, pero no he podido encontrar una solución.
Ideas?
Gracias
-
llappall
Aunque esta publicación tiene casi cinco años, es posible que tenga otra solución a la antigua pregunta de cómo manejar las NullPointerException
s.
En una palabra:
end: {
List<People> people = family.getPeople(); if(people == null || people.isEmpty()) break end;
People person = people.get(0); if(person == null) break end;
Address address = person.getAddress(); if(address == null) break end;
PostalCode postalCode = address.getPostalCode(); if(postalCode == null) break end;
System.out.println("Do stuff");
}
Dado que todavía hay muchos códigos heredados en uso, el uso de Java 8 y Optional
no siempre es una opción.
Siempre que haya clases profundamente anidadas involucradas (JAXB, SOAP, JSON, lo que sea ...) y no se aplica la Ley de Demeter , básicamente hay que verificar todo y ver si hay posibles NPE al acecho.
Mi solución propuesta se esfuerza por ser legible y no debería utilizarse si no hay al menos 3 o más clases anidadas involucradas (cuando digo anidada, no me refiero a clases anidadas en el contexto formal). Dado que el código se lee más de lo que está escrito, un vistazo rápido a la parte izquierda del código hará que su significado sea más claro que el uso de declaraciones if-else profundamente anidadas.
Si necesita la otra parte, puede utilizar este patrón:
boolean prematureEnd = true;
end: {
List<People> people = family.getPeople(); if(people == null || people.isEmpty()) break end;
People person = people.get(0); if(person == null) break end;
Address address = person.getAddress(); if(address == null) break end;
PostalCode postalCode = address.getPostalCode(); if(postalCode == null) break end;
System.out.println("Do stuff");
prematureEnd = false;
}
if(prematureEnd) {
System.out.println("The else part");
}
Ciertos IDE romperán este formato, a menos que les indique que no lo hagan (vea esta pregunta ).
Sus condicionales deben estar invertidos: usted le dice al código cuándo debe romperse, no cuándo debe continuar.
Una cosa más: su código todavía es propenso a romperse. Debe usar if(family.getPeople() != null && !family.getPeople().isEmpty())
como primera línea en su código, de lo contrario, una lista vacía arrojará un NPE.
En lugar de usar nulo, puede usar alguna versión del patrón de diseño "objeto nulo". Por ejemplo:
public class Family {
private final PersonList people;
public Family(PersonList people) {
this.people = people;
}
public PersonList getPeople() {
if (people == null) {
return PersonList.NULL;
}
return people;
}
public boolean isNull() {
return false;
}
public static Family NULL = new Family(PersonList.NULL) {
@Override
public boolean isNull() {
return true;
}
};
}
import java.util.ArrayList;
public class PersonList extends ArrayList<Person> {
@Override
public Person get(int index) {
Person person = null;
try {
person = super.get(index);
} catch (ArrayIndexOutOfBoundsException e) {
return Person.NULL;
}
if (person == null) {
return Person.NULL;
} else {
return person;
}
}
//... more List methods go here ...
public boolean isNull() {
return false;
}
public static PersonList NULL = new PersonList() {
@Override
public boolean isNull() {
return true;
}
};
}
public class Person {
private Address address;
public Person(Address address) {
this.address = address;
}
public Address getAddress() {
if (address == null) {
return Address.NULL;
}
return address;
}
public boolean isNull() {
return false;
}
public static Person NULL = new Person(Address.NULL) {
@Override
public boolean isNull() {
return true;
}
};
}
etc etc etc
Entonces tu sentencia if puede convertirse en:
if (!family.getPeople().get(0).getAddress().getPostalCode.isNull()) {...}
Es subóptimo ya que:
- Estás atascado haciendo objetos NULOS para cada clase,
- Es difícil hacer que estos objetos sean genéricos, por lo que te quedas atascado haciendo una versión de objeto nulo de cada Lista, Mapa, etc. que quieras usar, y
- Hay potencialmente algunos problemas divertidos con la subclasificación y qué NULL usar.
Pero si realmente odias tu == null
s, esta es una salida.
Lo más cerca que puede acercarse es aprovechar las reglas de atajo en los condicionales:
if(family != null && family.getPeople() != null && family.people.get(0) != null && family.people.get(0).getAddress() != null && family.people.get(0).getAddress().getPostalCode() != null) {
//FINALLY MADE IT TO DO SOMETHING!!!
}
Por cierto, atrapar una excepción en lugar de probar la condición de antemano es una idea horrible.
No es una buena idea, pero ¿qué hay de atrapar la excepción?
try
{
PostalCode pc = people.get(0).getAddress().getPostalCode();
}
catch(NullPointerException ex)
{
System.out.println("Gotcha");
}
Si es raro, puede ignorar las comprobaciones null
y confiar en NullPointerException
. "Raro" debido a un posible problema de rendimiento (depende, por lo general, completará el seguimiento de la pila, lo que puede ser costoso).
Aparte de eso 1) un método auxiliar específico que verifica si hay un nulo para limpiar ese código o 2) Haga un enfoque genérico utilizando la reflexión y una cadena como:
checkNonNull(family, "people[0].address.postalcode")
Implementación dejada como ejercicio.
Si, en el caso, está utilizando java8, entonces puede usar;
resolve(() -> people.get(0).getAddress().getPostalCode());
.ifPresent(System.out::println);
:
public static <T> Optional<T> resolve(Supplier<T> resolver) {
try {
T result = resolver.get();
return Optional.ofNullable(result);
}
catch (NullPointerException e) {
return Optional.empty();
}
}
REF: evitar cheques nulos
Solo estaba buscando lo mismo (mi contexto: un montón de clases JAXB creadas automáticamente, y de alguna manera tengo estas largas cadenas de margaritas de .getFoo().getBar()...
Invariablemente, de vez en cuando uno de Las llamadas en el medio devuelven nulo, causando NPE.
Algo con lo que comencé a juguetear hace un tiempo se basa en la reflexión. Estoy seguro de que podemos hacer esto más bonito y más eficiente (almacenar en caché la reflexión, por una parte, y también definir métodos "mágicos" como ._all
para iterar automáticamente todos los elementos de una colección, si se ._all
algún método en el medio Una colección). No es bonito, pero tal vez alguien podría decirnos si ya hay algo mejor:
/**
* Using {@link java.lang.reflect.Method}, apply the given methods (in daisy-chain fashion)
* to the array of Objects x.
*
* <p>For example, imagine that you''d like to express:
*
* <pre><code>
* Fubar[] out = new Fubar[x.length];
* for (int i=0; {@code i<x.length}; i++) {
* out[i] = x[i].getFoo().getBar().getFubar();
* }
* </code></pre>
*
* Unfortunately, the correct code that checks for nulls at every level of the
* daisy-chain becomes a bit convoluted.
*
* <p>So instead, this method does it all (checks included) in one call:
* <pre><code>
* Fubar[] out = apply(new Fubar[0], x, "getFoo", "getBar", "getFubar");
* </code></pre>
*
* <p>The cost, of course, is that it uses Reflection, which is slower than
* direct calls to the methods.
* @param type the type of the expected result
* @param x the array of Objects
* @param methods the methods to apply
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T[] apply(T[] type, Object[] x, String...methods) {
int n = x.length;
try {
for (String methodName : methods) {
Object[] out = new Object[n];
for (int i=0; i<n; i++) {
Object o = x[i];
if (o != null) {
Method method = o.getClass().getMethod(methodName);
Object sub = method.invoke(o);
out[i] = sub;
}
}
x = out;
}
T[] result = (T[])Array.newInstance(type.getClass().getComponentType(), n);
for (int i=0; i<n; i++) {
result[i] = (T)x[i];
}
return result;
} catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
Tu código se comporta igual que
if(family != null &&
family.getPeople() != null &&
family.people.get(0) != null &&
family.people.get(0).getAddress() != null &&
family.people.get(0).getAddress().getPostalCode() != null) {
//My Code
}
Gracias a la evaluación de cortocircuitos , esto también es seguro, ya que la segunda condición no se evaluará si la primera es falsa, la tercera no se evaluará si la segunda es falsa, ... y no obtendrá NPE porque si eso.