Obtener un valor sin tener que anular la comprobación en Java

15

Muchas veces me encuentro con una comprobación nula cuando obtengo un valor de alguna jerarquía de datos para evitar NullPointerExceptions, que encuentro propenso a errores y necesita mucha repetitiva.

He escrito una rutina muy simple que me permite omitir la comprobación nula al buscar un objeto ...

public final class NoNPE {

    public static <T> T get(NoNPEInterface<T> in) {
        try {
            return in.get();
        } catch (NullPointerException e) {
            return null;
        }
    }

    public interface NoNPEInterface<T> {
        T get();
    }
}

Lo uso un poco así ...

Room room = NoNPE.get(() -> country.getTown().getHouses().get(0).getLivingRoom());

Lo anterior resulta en que obtengo un objeto Room o un valor nulo, sin tener que anular todos los niveles principales.

¿Qué opinas de lo anterior? ¿Estoy creando un patrón problemático? ¿Hay una mejor manera de hacer esto en tu opinión?

Eurig Jones
fuente
1
Como aparentemente está utilizando Java 8, ¿puedo sugerirle que considere rediseñar su aplicación para usarla en java.util.Optionallugar de valores nulos para representar datos faltantes? Esto proporciona utilidades útiles tanto para el caso que describe como para los casos en que desea continuar con los datos predeterminados en lugar de simplemente devolver una condición de falla al final de la cadena ...
Periata Breatta
Creo que esencialmente has redescubierto la Option(o Maybe) mónada :)
Andres F.
Es posible que devuelva Opcional en lugar de T o nulo: de esta manera puede usar el método orElse () directamente. 18 meses después, pero podría ayudar a alguien.
Benj
Se mencionan otros enfoques en esta publicación illegalargumentexception.blogspot.com/2015/03/… , uno de ellos está utilizando una biblioteca llamada kludje que tiene una sintaxis muy interesante
Benj

Respuestas:

13

Tu solución es muy inteligente. El problema que veo es el hecho de que usted no sabe por qué recibió una null? ¿Fue porque la casa no tenía habitaciones? ¿Fue porque el pueblo no tenía casas? ¿Fue porque el país no tenía ciudades? ¿Fue porque hubo un nullen la posición 0 de la colección debido a un error incluso cuando hay casas en las posiciones 1 y superiores?

Si hace un uso extensivo de la NonPEclase, tendrá serios problemas de depuración. Creo que es mejor saber exactamente dónde se rompe la cadena que obtener silenciosamente algo nullque podría estar ocultando un error más profundo.

También esto viola la Ley de Demeter : country.getTown().getHouses().get(0).getLivingRoom(). La mayoría de las veces, violar algún buen principio hace que tenga que implementar soluciones poco ortodoxas para resolver el problema causado por la violación de dicho principio.

Mi recomendación es que lo use con precaución e intente resolver la falla de diseño que le obliga a incurrir en el antipatrón de choque de trenes (para que no tenga que usarlo en NonPEtodas partes). De lo contrario, puede tener errores que serán difíciles de detectar.

Tulains Córdova
fuente
Gran respuesta. Sí, no sabré de dónde saqué el nulo en la cadena. En muchos casos, aunque no me importa y no tener que hacer una verificación nula, significa que el código es más legible y menos propenso a errores repetitivos. Pero sí, tiene razón en algunos casos en los que necesito tomar una decisión lógica diferente si un objeto padre es nulo, esto podría causar un problema. El método convencional o la clase Opcional podría ser una solución más segura allí.
Eurig Jones
En general, cuando usa la Optionmónada, no le importa en qué parte de la cadena se encuentre el valor ausente. Cuando te preocupes por eso, probablemente usarías un tipo diferente, como Either.
Andres F.
El enfoque del OP es similar al de los C # 6 ?.y los ?[]operadores. Un ejemplo de cuándo podría querer usar tal cosa es la configuración jerárquica del lado del servidor. var shouldDoThing = settings?.a?.b?.c ?? defaultSetting;¿A quién le importa por qué alguna parte de eso fue nula? Tal vez no pudiste obtener la configuración. Tal vez decidió eliminar una sección de la configuración. En cualquier caso, nunca puede contar con obtener la configuración del servidor, por lo que una opción predeterminada suele ser una buena idea, y es poco probable que le importe por qué no pudo obtener la configuración real a menos que ocurra con mucha frecuencia cuando no debería .
Chris
Ahora no digo que sea estrictamente mejor o peor que localizar los valores predeterminados y simplemente recuperar el valor que desea a través de los accesos normales settings.a.b.c. Por otra parte, este es un solo ejemplo aislado.
Chris
10

La idea está bien, de hecho muy buena. Dado que Java 8 Optionalexisten los tipos, se puede encontrar una explicación detallada en el tipo Java Opcional . Un ejemplo con lo que publicaste es

Optional.ofNullable(country)
    .map(Country::getTown)
    .map(Town::Houses);

Y más adelante.

J. Pichardo
fuente
1
Sí, conocía la clase Opcional, tanto de Java 8 como de Guava, y son realmente útiles. Pero no puede buscar un objeto, ya que normalmente haría que el código sea un poco más difícil de leer y un poco menos eficiente también. Pero lo bueno es que hay muchos operadores muy útiles que proporciona la clase Opcional.
Eurig Jones
33
@EurigJones No creo que el código se vuelva menos eficiente. La legibilidad en el ojo del espectador, pero diría que Optionales la solución más legible de las dos, aunque solo sea porque, a diferencia de su propuesta, es un idioma muy común . ¡Es aún más conciso que el tuyo!
Andres F.
0

Su método funciona lo suficientemente bien para su propósito previsto, aunque devuelve nulls cuando obtiene un NullPointerExceptionsonido como un mal diseño.

Intente evitar nulls cuando pueda y solo páselos cuando representen algo o tengan un significado especial y solo devuélvalos cuando representen / signifiquen algo; de lo contrario, debe lanzar a NullPointerException. Esto evita errores y confusión. Si un Objectno debería ser null, un NullPointerdebería ser arrojado. Si un objeto puede ser, nullentonces nada saldrá mal cuando se pasa uno. De lo contrario, su método anterior funciona.

Luke Melaia
fuente
0

Puedo sentir tu dolor, pero la solución propuesta es una mala idea.

  • Si uno de los captadores arroja un NPE por alguna otra razón, lo ignorará.
  • Existe el riesgo de que ese lambda interno se convierta en un código horrible. Por ejemplo, si hay un nuevo requisito para devolver una constante especial cuando no hay casas en la ciudad, un programador perezoso puede extender la lamda, dejando todo envuelto NoNPE.get.
  • Como ya se mencionó, Optional.mapes lo que estás buscando.
  • La penalización de crear una nueva instancia de NullPointerException es a menudo significativa. Son muchos microsegundos, especialmente a medida que su pila de llamadas se está haciendo más grande. Es difícil predecir dónde se utilizará su utilidad.

Como nota al margen, NoNPEInterfacees un duplicado de java.util.function.Supplier.

En algunos casos, podría considerar usar una utilidad de evaluación de expresiones que esté presente en muchos marcos (por ejemplo: EL, SpEL):

evaluateProperty(country, "town.houses[0].livingRoom")
Mateusz Stefek
fuente
Ok para las plantillas de página web, pero en general es lento para desarrollar (sin verificación de tiempo de compilación) y lento para ejecutarse.
Kevin Cline