Java 如何将代码重写为optionals?

Java 如何将代码重写为optionals?,java,nullpointerexception,java-8,optional,Java,Nullpointerexception,Java 8,Optional,在我目前的工作中,我们正在将一些代码重写为Java8。 如果您有这样的代码: if(getApi() != null && getApi().getUser() != null && getApi().getUser().getCurrentTask() != null) { getApi().getUser().getCurrentTask().pause(); } 您只需将其改写为 Optional.ofNullable(this.ge

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

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呢?< /P> 例如:

if(getApi() != null && getApi().getUser() != null 
     && getApi().hasTasks())  
{
   getApi().getMasterUser(getApi().getUser()) //<- npe can be here
     .getCurrentTask().pause();
}
它可以检查api、用户、老板,但不能检查部门。如何使用optionals制作

if(getApi() != null && getApi().getUser() != null) {
    if(getApi().getUser().getDepartment().getBoss() != null) {
        getApi().getUser().getDepartment().getBoss().somefunc();
    }
}
使用optionals书写此内容的一种方法是:

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();
}
在这里,您需要同时访问
api
user
,因此您可能需要嵌套lambda:

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

第一个例子的答案是

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);
第二个例子:

if(getApi()!=null && getApi.getUser() != null)
{
   if(getApi().getUser().getDepartment().getBoss() != null)// <- nre if department is null
     {
        getApi().getUser().getDepartment().getBoss().somefunc();
     }
 }
Optional.ofNullable(getApi())
.map(Api::getUser)
.map(u -> Objects.requireNonNull(u.getDepartment()))
.map(Department::getBoss)
.ifPresent(Boss::somefunc);
因此,您必须将
.map(class::func)
更改为
.map(o->Objects.requirennoull(o.func())
,使其在需要时抛出NRE

它当然打破了单子模式,但它仍然比没有任何解决方案要好

如果我错了,请纠正我

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

Optional.ofNullable(getApi())
.map(Api::getUser)
.flatMap(u -> Optional.ofNullable(u.getDepartment().getBoss()))
.ifPresent(Boss::somefunc);
它还依赖较少的API


我还想对你的“这打破了monad模式”发表评论——这里没有任何东西(包括你的解决方案)打破了monad模式。它完全可以用
返回
>=
表示。如果有什么区别的话,那就是调用ifPresent将其中断,因为它意味着副作用。

第一行代码有一个按位的,对吗?当然是错误的@MikeIt绝不是相同的
Optional.ofNullable(this::getApi)
甚至不编译。要修复它,您需要显式转换:
Optional.ofNullable((供应商)this::getApi)
,如果实际的
getApi()
方法返回
null
Optional.ofNullable(this.getApi())
将返回
false
,而
Optional.ofNullable((供应商)this::getApi)
将始终返回
true
,无论方法的实现如何。@FedericoPeraltaSchaffner感谢您提供的信息。我以前遵循的指南似乎有点撒谎:)orElseThrow()展开可选的,所以你必须在之后将其包装回可选的,这样会使代码变得丑陋,尤其是当你不止一次必须检查它时,第二个看起来非常接近我需要的,但它需要重写遗留代码和所有使用这些函数的代码,这通常是不可能的。因此,如果调用了
map\u func
并返回null?@maxpovver,则调用
error\u func
的类似
.maprict(map\u func,error\u func)
的函数是:
map(user->Objects.requirennull(user.getDepartment())
。谢谢。请将注释中的代码添加到答案中,以便我可以接受它。它仍然不是完美的,因为monad应该检查它,而不是应用于monad的函数,但是它总比没有好。