我有一个User
和相关的有时间限制的Role
s。我想知道一个User
是否有一个特定的UserRole
* 并且 * 它是未过期的。我可以把用户的角色转换成一个流,filter()
它和findFirst()
,给我一个Optional
。
角色
public class Role {
private UserRole role;
private Date expiry;
public boolean isUnexpired () {
return (expiry == null) ? true : expiry.after(new Date());
}
}
用户
public class User {
//...
private Collection<Role> roles
public boolean hasRole (UserRole userRole) {
return roles.stream()
.filter(r -> r.getRole().equals(userRole))
.findFirst()
.ifPresent(ur -> { /* ... herein the problem ... */ ur.isUnexpired(); } );
}
}
最后一行的问题是ifPresent()
有一个无效签名;因此,我不能从它返回ur.isUnexpired()
。无论我在lambda表达式或匿名内部类中放置什么,都不能对它找到的值做任何有意义的事情。
我尝试在过滤流之前声明一个布尔值,并赋值,但得到(代码验证)错误:local variables referenced from a lambda expression must be final or effectively final
.
(我知道,当它不存在时,有更多的事情要处理;如果我能对它排序,我就可以交换到ifPresentOrElse()
。)
我可以这样做:
public boolean hasRole (UserRole userRole) {
Optional<Role> o = roles.stream()
.filter(r -> r.getRole().equals(userRole))
.findFirst();
return o.isPresent() ? o.get().isUnexpired() : false;
}
但是,我宁愿用一个更干净的,链式的函数来做。
有没有什么方法可以提取并使用我的isUnexpired()
布尔值和一个链式函数?或者我必须分配Optional
然后单独操作它?
3条答案
按热度按时间34gzjxbg1#
应该使用
Optional::map
来检索isUnexpired
的值,使用orElse
来返回false
:如果有 * 多个 * 相同的角色具有 * 不同的 * 到期日期(@Ray提到的有点奇怪的用例),可能需要按到期日期(降序)进行额外的排序来解决冲突:
iezvtpos2#
如果你只关心用户是否在
Collection<Role> roles
中有任何未过期的UserRole
条目,使用anyMatch()
匹配 predicate 是一种更清晰、更简洁的方法:这种使用
anyMatch()
的方法对于将来的代码读者来说是清楚的,没有任何歧义。为什么不使用stream/filter/findFirst方法呢?仅基于OP中的信息,
private Collection<Role> roles
中可能有多个条目具有相同的UseRole和不同的到期日期。findFirst()
将产生不确定的结果,除非您采取措施确保集合的顺序(集合本身对订单没有保证)或者在过滤器中进行过期检查。即使使用签入过滤器,我认为anyMatch()
对您的意图不太开放。h79rfbju3#
但是,我宁愿用一个更干净的,链式的函数来做。
为什么一个链式函数是“更干净”的?正如你所看到的,它使代码适应不断变化的需求变得更加困难,并且在你的其他代码上强制执行奇怪的风格选择,以便绕过你不能使用可变的局部变量,也不能获得控制流或检查异常透明性的事实。我不知道你正在研究的“更干净”的定义是什么,但很明显,它不是“代码导致更容易修改,更容易测试,更容易阅读,更容易在代码中满足其他需求”,这对我来说似乎是一个更明智的定义。也许你的定义是基于美学。嗯,正如他们所说,你不能与品味争论,也许。
无论如何,你有两个选择:
将optional.NONEMap到一个sentinel上,然后该sentinel测试失败。
你可以在这里使用
.orElse()
:Map可选。SOME
map
调用将返回Optional.NONE,或者Boolean.TRUE
的Optional或Boolean.FALSE的可选值。然后我们将NONE情况转换为FALSE,现在我们有一个布尔值要返回。我认为第一个代码片段更容易理解,但它需要一个肯定过期的虚拟角色。
注意:如果你关心干净的函数,那么在布尔方法名中使用负数并不是一个好主意,原因很明显。也许
isLive()
或isValid()
比Unexpired
更好。