如何将代码重写为可选?

在我目前的工作中,我们正在将一些代码重写为Java 8。如果您有这样的代码:

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

你可以简单地将其重写为

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

无需更改代码行为。但是,如果中间的某些东西可以抛出NPE,因为它没有被检查为空,该怎么办?

例如:

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

使用可选选项重写此类代码的最佳方法是什么?(它应该工作完全相同,并在返回 null 时抛出 npe)getMasterUser(...)

UPD 第二个例子:

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

它对api,user,boss有空检查,但没有部门。如何使用可选选项进行制作?


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

使用可选选项编写此内容的一种方法是:

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

但这很容易出错,因为它要求客户端跟踪什么是可选的,什么是不可选的。更好的方法是使 api 本身返回可选值,而不是可为 null 的值。然后客户端代码是:

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

这将使API中更清楚地知道哪些值应该是可选的,并使不处理它们成为编译时错误。

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

在这里,您需要同时访问,因此您可能需要嵌套 lambda:apiuser

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

答案 2

对于第二个示例(也适用于第一个示例),它更短,与较长的版本一样明显:

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

它还依赖于较少的 API。

我还想评论一下你的“这打破了monad模式”——这里没有任何东西(包括你的解决方案)会打破monad模式。它完全可以用 和 来表示。如果有的话,那就是呼叫打破了它,因为它暗示了副作用。return>>=ifPresent


推荐