validate util tutorial example ejemplo check java nullpointerexception java-8 optional

util - optional list java 8 example



¿Cómo reescribir el código a los opcionales? (3)

En mi trabajo actual, estamos reescribiendo algún código en Java 8. Si tiene un código como este:

if(getApi() != null && getApi().getUser() != null && getApi().getUser().getCurrentTask() != null) { getApi().getUser().getCurrentTask().pause(); }

simplemente puedes reescribirlo

Optional.ofNullable(this.getApi()) .map(Api::getUser) .map(User::getCurrentTask) .ifPresent(Task::pause);

sin cambiar el comportamiento del código. pero ¿qué pasa si algo en el medio puede lanzar NPE porque no está marcado en nulo?

por ejemplo:

if(getApi() != null && getApi().getUser() != null && getApi().hasTasks()) { getApi().getMasterUser(getApi().getUser()) //<- npe can be here .getCurrentTask().pause(); }

¿Cuál es la mejor manera de volver a escribir código como este usando opcionales? (debería funcionar exactamente igual y lanzar npe cuando getMasterUser(...) devuelve nulo)

Segundo ejemplo de la UPD :

if(getApi()!=null && getApi.getUser() != null) { if(getApi().getUser().getDepartment().getBoss() != null)// <- nre if department is null { getApi().getUser().getDepartment().getBoss().somefunc(); } }

tiene nullchecks para api, usuario, jefe, pero no departamento. ¿Cómo se puede hacer usando opcionales?


Así que la respuesta para el primer ejemplo es

Optional.ofNullable(getApi()) .filter(Api::hasTasks) .map(Api::getUser) .map(u -> Objects.requireNonNull(getApi().getMasterUser(u)))//api won''t be null here so no need to check it .map(MasterUser::getCurrentTask) .ifPresent(Task::pause);

y para el segundo ejemplo:

Optional.ofNullable(getApi()) .map(Api::getUser) .map(u -> Objects.requireNonNull(u.getDepartment())) .map(Department::getBoss) .ifPresent(Boss::somefunc);

Así que tienes que cambiar .map(class::func) por .map(o -> Objects.requireNonNull(o.func())) para hacer que se lance NRE cuando sea necesario.

Por supuesto, rompe el patrón de la mónada, pero sigue siendo mejor que ninguna solución.

Corrígeme si me equivoco por favor.


Para el segundo ejemplo (aplicable también al primero), esto es más corto y tan obvio como la versión más larga:

Optional.ofNullable(getApi()) .map(Api::getUser) .flatMap(u -> Optional.ofNullable(u.getDepartment().getBoss())) .ifPresent(Boss::somefunc);

También se basa en menos API.

Además, me gustaría comentar sobre su "esto rompe el patrón de la mónada"; nada aquí (incluidas sus soluciones) rompe el patrón de la mónada. Es totalmente expresable en términos de return y >>= . En todo caso, es la llamada ifPresent que la interrumpe porque implica efectos secundarios.


if(getApi() != null && getApi().getUser() != null) { if(getApi().getUser().getDepartment().getBoss() != null) { getApi().getUser().getDepartment().getBoss().somefunc(); } }

Una forma de escribir esto con opcionales es:

Optional.ofNullable(this.getApi()) .map(Api::getUser) .map(user -> Objects.requireNonNull(user.getDepartment())) .map(Department::getBoss) .ifPresent(Boss::somefunc);

Pero esto es propenso a errores porque requiere que el cliente realice un seguimiento de lo que es y no es opcional. Una mejor manera sería hacer que la propia API devuelva un valor opcional en lugar de un valor nulo. Entonces el código del cliente es:

this.getApi() .flatMap(Api::getUser) .map(user -> user.getDepartment().getBoss()) .ifPresent(Boss::somefunc));

Esto dejaría más claro en la api qué valores deberían ser opcionales y haría que no sea un error de tiempo de compilación no manejarlos.

if(getApi() != null && getApi().getUser() != null && getApi().hasTasks()) { getApi().getMasterUser(getApi().getUser()).getCurrentTask().pause(); }

Aquí, necesita acceso a la api y al user al mismo tiempo, por lo que probablemente necesite anidar las lambdas:

getApi().filter(Api::hasTasks).ifPresent(api -> { api.getUser().ifPresent(user -> { api.getMasterUser(user).getCurrentTask().ifPresent(Task::pause); }); });