如何将代码重写为可选项?
How to rewrite code to optionals?
在我目前的工作中,我们正在重写一些代码以 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 怎么办?
例如:
if(getApi() != null && getApi().getUser() != null
&& getApi().hasTasks())
{
getApi().getMasterUser(getApi().getUser()) //<- npe can be here
.getCurrentTask().pause();
}
使用可选值重写这样的代码的最佳方法是什么?(它应该完全相同并在 getMasterUser(...)
returns null 时抛出 npe)
UPD
第二个例子:
if(getApi()!=null && getApi.getUser() != null)
{
if(getApi().getUser().getDepartment().getBoss() != null)// <- nre if department is null
{
getApi().getUser().getDepartment().getBoss().somefunc();
}
}
它有 api、用户、老板的空值检查,但没有部门。如何使用选项来制作它?
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 本身 return 成为可选值而不是可为 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);
第二个例子:
Optional.ofNullable(getApi())
.map(Api::getUser)
.map(u -> Objects.requireNonNull(u.getDepartment()))
.map(Department::getBoss)
.ifPresent(Boss::somefunc);
所以你必须将 .map(class::func)
更改为 .map(o -> Objects.requireNonNull(o.func()))
以使其在需要时抛出 NRE。
它当然打破了 monad 模式,但总比没有解决方案好
如有错误请指正。
对于第二个例子(也适用于第一个),这个例子更短并且和更长的版本一样明显:
Optional.ofNullable(getApi())
.map(Api::getUser)
.flatMap(u -> Optional.ofNullable(u.getDepartment().getBoss()))
.ifPresent(Boss::somefunc);
它也依赖较少API。
我还想对您的 "this breaks the monad pattern" 发表评论——这里没有任何内容(包括您的解决方案)破坏 monad 模式。它完全可以用 return
和 >>=
来表达。如果有的话,是 ifPresent
调用打破了它,因为它意味着副作用。
在我目前的工作中,我们正在重写一些代码以 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 怎么办?
例如:
if(getApi() != null && getApi().getUser() != null
&& getApi().hasTasks())
{
getApi().getMasterUser(getApi().getUser()) //<- npe can be here
.getCurrentTask().pause();
}
使用可选值重写这样的代码的最佳方法是什么?(它应该完全相同并在 getMasterUser(...)
returns null 时抛出 npe)
UPD 第二个例子:
if(getApi()!=null && getApi.getUser() != null)
{
if(getApi().getUser().getDepartment().getBoss() != null)// <- nre if department is null
{
getApi().getUser().getDepartment().getBoss().somefunc();
}
}
它有 api、用户、老板的空值检查,但没有部门。如何使用选项来制作它?
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 本身 return 成为可选值而不是可为 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);
第二个例子:
Optional.ofNullable(getApi())
.map(Api::getUser)
.map(u -> Objects.requireNonNull(u.getDepartment()))
.map(Department::getBoss)
.ifPresent(Boss::somefunc);
所以你必须将 .map(class::func)
更改为 .map(o -> Objects.requireNonNull(o.func()))
以使其在需要时抛出 NRE。
它当然打破了 monad 模式,但总比没有解决方案好
如有错误请指正。
对于第二个例子(也适用于第一个),这个例子更短并且和更长的版本一样明显:
Optional.ofNullable(getApi())
.map(Api::getUser)
.flatMap(u -> Optional.ofNullable(u.getDepartment().getBoss()))
.ifPresent(Boss::somefunc);
它也依赖较少API。
我还想对您的 "this breaks the monad pattern" 发表评论——这里没有任何内容(包括您的解决方案)破坏 monad 模式。它完全可以用 return
和 >>=
来表达。如果有的话,是 ifPresent
调用打破了它,因为它意味着副作用。