Java-需要帮助解开紧凑的Java符号:orElse、Optional、Lazy
我试图了解这段 Java 代码中发生了什么,因为它的所有者不再存在并且可能正在修复或简化它。我猜这些块在某个时候有更多的东西,剩下的东西没有被正确清理。似乎所有出现的orElse(false)都没有设置任何内容false并且可以删除。然后第二种removeDiscontinued方法返回一个我认为不会在任何地方使用的布尔值。这只是我,还是写得很难读?由于我没有使用过像 orElse、Lazy、Optional 这样的语法,所以我很犹豫要不要删除它。一些帮助将不胜感激。
private void removeDiscontinued(Optional<Map<String, JSONArrayCache>> dptCache, Lazy<Set<String>> availableTps) {
dptCache.map(pubDpt -> removeDiscontinued(pubDpt.keySet(), availableTps)).orElse(false);
}
private boolean removeDiscontinued(Set<String> idList, Lazy<Set<String>> availableTps) {
if (availableTps.get().size() > 0) {
Optional.ofNullable(idList).map(trIds -> trIds.removeIf(id -> !availableTps.get().contains(id)))
.orElse(false);
}
return true;
}
回答
这段代码确实非常愚蠢。我知道为什么 - 有一个有点普遍的,极其误导的运动。这一运动提出的主张通常被解释为“把它写成‘功能性的’,然后它就更好了”。
这种解释是明显的马尾气。这不是真的。
我们可以就谁应该为此负责进行辩论 - 是人们听到争论/阅读博文并得出错误的结论,还是“功能性粉丝”煽风点火,可以这么说,做出荒谬的主张撑不住?
要点是:这段代码在完全不合适的情况下使用了函数式风格,结果变成了正确的一团糟。代码肯定是坏的;这段代码的作者不是一个伟大的程序员,但也许大部分的责任都归咎于功能传播者。无论如何,它很难阅读;难怪你很难弄清楚这些东西的作用。
根本问题
根本问题是,这种函数式风格非常喜欢作为一个无副作用的过程:你从一些数据开始,然后函数式管道(流映射链、orElse 等操作)产生一些新的结果,然后你做一些事情接着就,随即。管道内的任何东西都不应该改变任何东西,它只是为计算新事物服务。
您的两种方法都无法正确执行- 它们都忽略了“管道”的返回值,这完全是关于副作用。
你不想要这个:管道的主要观点是他们可以跳过步骤,如果他们认为可以的话,他们会积极地这样做,并且管道假设没有副作用,所以它会做出错误的调用。
这orElse是不实际可选的-它似乎并没有做任何事情,不同的是:它迫使该管道运行,除了规格完全不是那么回事保证它会,所以这段代码是在这个意义上平出破碎了。
这些方法也Optional作为参数类型接受,这是完全错误的。Optional 可以作为功能管道的返回值(例如Stream's own max()etc 方法)。它作为返回值在其他任何地方都是有争议的,而且它很愚蠢而且样式错误非常糟糕,如果它们出现在字段声明或方法参数中,你应该配置你的 linter 积极地将其标记为不适合生产代码。
所以也摆脱它。
让我们分解一下这些方法的作用
他们都将调用mapOptional。一个可选项要么是'NONE',它就像空值(如,没有值),或者它是一个SOME,这意味着只有一个值。
你的两种方法都调用map了一个可选的。在这些特定方法中,此操作或多或少归结为:
如果可选项为 NONE,则什么也不做,静默。否则,执行括号中的操作。
因此,要摆脱Optional第一个方法的参数,只需删除它,然后更新调用代码,以便它决定在没有值的情况下做什么,而不是这对方法(决定:如果通过在一个 optional.NONE 中,默默地什么都不做。“Silently do nothing”是一种极其愚蠢的默认行为模式,这也是 Optional 不是很好的很大一部分原因)。显然,它有一个来自某处的 Optional - 要么是它实现的(例如,Optional.ofNullable在这种情况下也取消它,或者它从其他地方得到一个,例如因为它执行流操作并返回一个可选项,在这种情况下,替换:
Optional<Map<String, JSONArrayCache>> optional = ...;
removeDiscontinued(thatOptionalThing, availableTps);
和:
optional.map(v -> removeDiscontinued(v, availableTps));
或者也许只是:
if (optional.isPresent()) {
removeDiscontinued(optional.get(), availableTps);
} else {
code to run otherwise
}
如果你不明白它怎么可能是空的,太好了!在许多情况下,Optional 比 NullPointerException 严重得多,因此它也在这里:当在所述代码的程序员不知道这种可能性的地方缺少某些值时,您不希望您的代码默默地做任何事情 -异常是非常优越的:然后您就知道存在问题,并且异常会告诉您在哪里。与“静默不做任何事”的方法相反,在这种方法中,很难说有什么不对劲,一旦你意识到有什么不对劲,你就不知道该往哪里看。发现问题需要花费数百倍的时间。
因此,那就去吧:
removeDiscontinued(thatOptionalThing.get(), availableTps);
如果发生意外,这将是 NPE,这很好。
方法本身
摆脱这些管道,函数式在这里不是正确的方法,因为您只对副作用感兴趣:
private void removeDiscontinued(Map<String, JSONArrayCache> dptCache, Lazy<Set<String>> availableTps) {
Set<String> keys = dptCache.keySet();
if (availableTps.get().size() > 0) {
keys.removeIf(id -> availableTps.get().contains(id));
}
}
就是这样 - 这就是你所需要的,这就是代码以一种非常奇怪的、草率的、边界破碎的方式所做的。
具体来说:
- 该
boolean返回值只是一个红鲱鱼 - 作者需要该代码返回某些内容,以便他们可以在map操作中将其用作参数。该值完全没有意义。如果一个风格指南承诺:“如果你使用这种风格编写它,你的代码会更好”最终导致非常混乱的无意义变量,其值无关紧要,我认为摆脱风格指南。 - 该
ofNullable包装是没有意义的:这种方法是私人的,它唯一的调用者不可能通过空在那里,除非dptCache是地图界面,屈尊返回的一些比扎罗碎实现的实例,null其时keySet()被调用的方法:如果是这样的情况发生,肯定解决问题在源代码中,不要在您的代码库中解决它,没有理智的 Java 读者会期望.keySet在那里返回 null。这ofNullable只是让这些东西难以阅读,它在这里没有任何作用。 - 请注意,
if (availableTps.get().size() > 0)检查只是一种优化。如果你愿意,你可以把它去掉。除非 dptCache 对象是一个大映射(至少有数千个键),否则该优化不会产生任何影响。