我一直在尝试对我的代码库中这段看起来非常丑陋的代码进行一些修改。 原始代码如下所示:
Long variable = Optional.of(message)
.map(Message::getPayload)
.map(MessageContentClass::getVariable)
.orElse(null);
由于它在代码库中重复了很多次,这真的很烦我。我已经设法将其简化为这样的函数:
public <T, K> T getVariableFromMessage(Message<K> message, Function<K, T> func) {
return Optional.of(message)
.map(Message::getPayload)
.map(func)
.orElse(null);
}
而且效果很好。但是,还有一些消息类型,其中所需的变量嵌套在消息中更深一些。大致如下:
Long variable = Optional.of(message)
.map(Message::getPayload)
.map(MessageContentClass::getObject1)
.map(Object1::getObject2)
.map(Object2::getVariable)
.orElse(null);
我什至不知道是否可能,但是泛化第一个函数会很简洁,这样我就有(而不是单个函数
希望你能清楚地了解我想要做什么,并感谢您阅读整篇文章:)
您需要一个映射函数,一次性从头到尾进行映射,在每个中间步骤都经历“可选”。
这很难。
Long variable = Optional.of(message)
.map(Message::getPayload)
.map(MessageContentClass::getObject1)
.map(Object1::getObject2)
.map(Object2::getVariable)
.orElse(null);
必须变成:
Function<Message, Long> func = m -> m.getPayload().getObject1().getObject2().getVariable();
这样你就可以直接走了
Optional.of(message).map(func).orElse(null);
但技巧是你的函数需要围绕空指针异常跳舞。一种选择是实际捕获它们,如果发生则返回可选的无,但是,这也会也捕获由 getter 本身直接抛出的任何 NPE;使用你的 bog 标准
T getName() { return this.name; }
样式 getter 不会发生,但你的代码无法区分链中返回 null
的 getter 和抛出 NPE 的 getter itself 之间的区别,这仍然令人厌恶。如果你想要的话:
public <I, O> Optional<O> longMap(Optional<I> in, Function<I, O> func) {
return in.flatMap(i -> {
try {
return Optional.of(func.apply(i));
} catch (NullPointerException e) {
return Optional.empty();
}
});
}
武装起来:
Long variable = longMap(Optional.of(message),
m -> m.getPayload().getObject1().getObject2().getVariable())
.orElse(null);
但是,使用
Optional
围绕 null 跳舞只是一个坏主意。如果您的代码库抛弃了空值,那么修复代码库,或者拥抱它。这个半解决方案让你了解两者的缺点,两者的优点,以及诸如这个问题之类的棘手问题作为奖励。 (苹果?)
如果必须的话,请更清楚地表明这就是您所构建的:
static <O> Optional<O> nullSafe(Supplier<O> op) {
try {
return Optional.of(op.get());
} catch (NullPointerException e) {
return Optional.empty();
}
}
这可以让你做:
long v = nullSafe(() ->
message
.getPayload()
.getObject1()
.getObject2()
.getLongVar()).orElse(10);
(通常,如果你写
orElse(null)
,你就搞砸了)。
或者,倡导 elvis 操作员或类似的东西;目前它不在java中,也不在路线图上,大约十年前它曾在路线图上并被拒绝。你“想要”的是:
long v = message?.getPayload()?.getObject1()?.getObject2()?.getLongVar() ?: 10;
其中,如果
message?.getPayload()
为 null,则 null
计算为 message
,而不是导致 NPE;如果 x 不为空,则 x ?: 10
计算为 x
,否则为 10。如果整个链都不为空,则此代码的效果是为您提供 getLongVar()
的值,如果其中任何部分为空,则为 10。
需要明确的是,这是不存在的,并且没有简单的方法可以做到这一点;确实存在的几种方法涉及捕获 NPE,如本答案中所示的代码所示。
您无法直接实现此目的,至少不能以类型安全的方式实现。
Function.andThen()
间接链接映射操作。
返回一个组合函数,该函数首先将此函数应用于其输入,然后将 after 函数应用于结果。如果任一函数的计算引发异常,则会将其转发给组合函数的调用者。
Function<MessageContentClass> first = MessageContentClass::getObject1;
Function<MessageContentClass, Variable> final = first
.andThen(Object1::getObject2)
.andThen(Object2::getVariable);
Variable variable = getVariableFromMessage(message, final);