Java 如何将代码重写为optionals?
在我目前的工作中,我们正在将一些代码重写为Java8。 如果您有这样的代码: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
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的函数,但是它总比没有好。