Java中从if-elseif-else获取值的函数变体

Java中从if-elseif-else获取值的函数变体,java,java-8,functional-programming,Java,Java 8,Functional Programming,我必须处理一些json,它们的格式可能略有不同(我只需要json数据的一个子集),我使用JsonPointer(来自Jackson)来查询json。我为这个问题写了一个非功能性的解决方案,这对我来说很有效,但我想尝试一种功能性的方法来进行学习。在下面的测试程序中,您可以看到我的两个解决方案。它们都可以工作,但是函数解决方案变得非常冗长,Intellij发出了一个恼人的警告,关于在没有isPresent检查的情况下使用get()。我希望看到如何改进功能实现的建议,我很高兴看到使用第三方库的解决方案

我必须处理一些json,它们的格式可能略有不同(我只需要json数据的一个子集),我使用JsonPointer(来自Jackson)来查询json。我为这个问题写了一个非功能性的解决方案,这对我来说很有效,但我想尝试一种功能性的方法来进行学习。在下面的测试程序中,您可以看到我的两个解决方案。它们都可以工作,但是函数解决方案变得非常冗长,Intellij发出了一个恼人的警告,关于在没有isPresent检查的情况下使用get()。我希望看到如何改进功能实现的建议,我很高兴看到使用第三方库的解决方案。我想,这里的基本问题是如何建模if-else-if-else,其中每个分支都应该以函数的方式返回一些值

@Test
public void testIt() {
    ObjectMapper om = new ObjectMapper();
    ImmutableList.of(
            "{ \"foo\": { \"key\": \"1\" } }",
            "{ \"bar\": { \"key\": \"1\" } }",
            "{ \"key\": \"1\" }")
            .forEach(str -> {
                try {
                    System.out.println("Non-functional: " + getNode(om.readTree(str)));
                    System.out.println("Functional: " + getNodeFunc(om.readTree(str)));
                } catch (Exception e) {
                    throw new RuntimeException("", e);
                }
            });
}

private JsonNode getNode(JsonNode parentNode) {
    JsonPointer jp1 = JsonPointer.compile("/foo");
    JsonPointer jp2 = JsonPointer.compile("/bar");
    if (!parentNode.at(jp1).isMissingNode()) {
        return parentNode.at(jp1);
    } else if (!parentNode.at(jp2).isMissingNode()) {
        return parentNode.at(jp2);
    }
    return parentNode;
}

private JsonNode getNodeFunc(JsonNode parentNode) {
    BiFunction<JsonNode, String, Optional<JsonNode>> findNode = (node, path) -> {
        JsonPointer jp = JsonPointer.compile(path);
        return node.at(jp).isMissingNode() ? Optional.empty() : Optional.of(node.at(jp));
    };

    return findNode.apply(parentNode, "/foo")
            .map(Optional::of)
            .orElseGet(() -> findNode.apply(parentNode, "/bar"))
            .map(Optional::of)
            .orElse(Optional.of(parentNode))
            .get(); // Intellij complains here: Optional.get() without isPresent check
}
@测试
公开无效测试(){
ObjectMapper om=新的ObjectMapper();
不可变列表(
“{\'foo\':{\'key\':\'1\'}”,
“{\'bar\':{\'key\':\'1\'}”,
“{\'key\':\'1\'}”)
.forEach(str->{
试一试{
System.out.println(“非功能:+getNode(om.readTree(str));
System.out.println(“Functional:+getNodeFunc(om.readTree(str));
}捕获(例外e){
抛出新的运行时异常(“,e);
}
});
}
私有JsonNode getNode(JsonNode parentNode){
JsonPointer jp1=JsonPointer.compile(“/foo”);
JsonPointer jp2=JsonPointer.compile(“/bar”);
if(!parentNode.at(jp1.isMissingNode()){
返回parentNode.at(jp1);
}如果(!parentNode.at(jp2.isMissingNode()),则为else{
返回parentNode.at(jp2);
}
返回父节点;
}
私有JsonNode getNodeFunc(JsonNode parentNode){
双功能findNode=(节点,路径)->{
JsonPointer jp=JsonPointer.compile(路径);
return node.at(jp).isMissingNode()?Optional.empty():Optional.of(node.at(jp));
};
返回findNode.apply(parentNode,“/foo”)
.map(可选::of)
.OrelGet(()->findNode.apply(parentNode,“/bar”))
.map(可选::of)
.orElse(可选的.of(parentNode))
.get();//Intellij在此抱怨:可选。get()不带isPresent检查
}
我会把它改写成

private JsonNode getNodeFunc2(JsonNode parentNode) {
    return Stream.of(JsonPointer.compile("/foo"), JsonPointer.compile("/bar"))
                 .filter(i -> !parentNode.at(i).isMissingNode())
                 .findFirst()
                 .map(parentNode::at)
                 .orElse(parentNode);
}

因为那块

if (!parentNode.at(jp1).isMissingNode()) {
    return parentNode.at(jp1);
} else if (!parentNode.at(jp2).isMissingNode()) {
    return parentNode.at(jp2);
}
是代码重复,可以通过循环整齐地处理:

for (JsonPointer jsonPointer : jsonPointers) {
    JsonNode kid = parentNode.at(jsonPointer);
    if (!kid.isMissingNode()) {
         return kid;
    }
}

需要考虑的是,
getNode
函数是完美的函数代码:

  • 输出仅取决于其输入参数和内部算法
  • 它没有副作用(它不从外界读任何东西,也不向外界写任何东西)
  • 它总是从相同的输入返回相同的输出
非功能代码有什么问题?在我看来非常好。当我试图用Java编写函数式代码时,我觉得我一次又一次学到的东西是Java中有多少更丑陋(大多数)的函数式代码;)为什么要使用
map(可选::of)
findNode.apply()
返回一个
可选的
,所以你不需要使用
map()
@AndyTurner,我一开始也有这种感觉,但在groovy中使用它一段时间后,现在是java,无法编写函数似乎真的很尴尬和限制。很难解释,直到你做了几年,并在你的大脑中建立了一些新的途径。。。特别令人沮丧的是,if()、try/catch和switch之类的东西不会返回一个值,这会导致难看的语法,比如在开关或try/catch中设置变量之前将变量定义为null。if()使用?:作为函数等价物,但如果拆分行,语法是独特而笨拙的。@AndyTurner试着从更高的层次看它——它读起来像一本书。过滤它(除去不存在的节点),找到第一个,返回parentNode.at或parentNode(如果不存在)。对我来说,这非常容易阅读,也许更难写作。将其与问题进行比较,看看哪个更具可读性。我强烈推荐拥抱而不是争斗:)Predicate.not()是Java11吗?不幸的是,在我工作的项目中,我们仍然使用Java8,但希望在不久的将来升级。非常有帮助的回答,我从中学到了,这是我回答这个问题的目的,谢谢!
if (!parentNode.at(jp1).isMissingNode()) {
    return parentNode.at(jp1);
} else if (!parentNode.at(jp2).isMissingNode()) {
    return parentNode.at(jp2);
}
for (JsonPointer jsonPointer : jsonPointers) {
    JsonNode kid = parentNode.at(jsonPointer);
    if (!kid.isMissingNode()) {
         return kid;
    }
}