Java中If Else语句的简写形式

28

我有一种检查空值的方法。是否有办法减少该方法中的代码行数?目前代码看起来很"杂乱无章":

private int similarityCount (String one, String two) {

    if (one == null && two == null) {
        return 1;
    } else if (one == null && two != null) {
        return 2;
    } else if (one != null && two == null) {
        return 3;
    } else {
        if(isMatch(one, two))
             return 4;
        return 5;
    }

}

7
如果在if块内有return值,那么你不需要使用else - Manh Le
1
你可以使用三元运算符。 - Sanjay
4
使用简写或if-else没有性能上的区别,更好的方式是编写易读的代码,让人们理解你的代码。上面的代码已经足够清晰和好理解了。 - PSo
1
你说你想要减少代码行数。原因是你的示例代码有代码味道。减少行数并不能解决这个问题,减少复杂度才是关键。 - sbecker
免责声明:我不是Java专家。但我非常确定每个Algol衍生语言都有switch...另外,如果您的代码仍然需要显式的空值检查,那么类型系统的意义是什么? - Jared Smith
显示剩余3条评论
9个回答

61
private int similarityCount (String one, String two) {

    if (one == null && two == null) {
        return 1;
    } 

    if (one == null) {
        return 2;
    } 

    if (two == null) {
        return 3;
    } 

    if (isMatch(one, two)) {
        return 4;
    }
    return 5;
}

5
在第二个if语句中,您甚至可以删除&& two!=null,因为当代码执行到那个部分时它通常不是null。在某些执行路径上(无论如何,默认情况下应该是这样),您还可以使用assert来断言变量的非空性。 - CAD97
这是更易读的选择。唯一让我内心完美主义者感到困扰的是,在第二种和第三种情况下,您必须多次执行相同的空值检查。不过,如果后来发现有问题,您总是可以稍后解决它。 - Erik Madsen
3
为什么isMatch(one, two)要使用三元运算符?为什么不仅仅使用if语句呢?这样做会更符合现有流程,读起来也更加流畅。 - marcelm
@CaptainMan 这就是 assert 应该使用的方式,但有些情况下你的假设并不正确,assert 会失败;这就是为什么它们要在 -ea 下运行。无论如何,如果一个 assert 失败了,你的代码就会出现问题,所以(至少在调试时)我更喜欢一个崩溃来告诉我错误的假设。 - CAD97
1
我不理解最后一行打破格式的逻辑。看起来最后一行应该是两行:`if(isMatch(one,two) { return 4; } return 5; - corsiKa
显示剩余2条评论

33

在这种情况下,我更喜欢使用嵌套条件:

private int similarityCount (String one, String two) {
    if (one==null) {
        if (two==null) {
            return 1;
        } else {
            return 2;
        }
    } else {
        if (two==null) {
            return 3;
        } else {
            return isMatch(one, two) ? 4 : 5;
        }
    }
}
当然,您可以通过使用更多的三元条件运算符来实现更短的版本。
private int similarityCount (String one, String two) {  
    if (one==null) {
        return (two==null) ? 1 : 2;
    } else {
        return (two==null) ? 3 : isMatch(one, two) ? 4 : 5;
    }
}

甚至可以说(现在这变得不太易读了):

private int similarityCount (String one, String two) {  
    return (one==null) ? ((two==null) ? 1 : 2) : ((two==null) ? 3 : isMatch(one, two) ? 4 : 5);
}

4
值得点赞的努力 - PSo
我更喜欢第二个。在我看来,嵌套的if-else使得第一个难以一眼看清。可能是因为最后一个返回语句与前面的条件之间距离太远了。 - JollyJoker
4
我必须说,我认为这种陈述逻辑的方式非常复杂。它将null检查分为两种一般情况,即one == nullone != null,但实际上,我们真正关心的是onetwo是否为null,或者两者都不是。因此,作为结果,我建议不要采用这里提出的任何选项。 - Konrad Rudolph
1
@KonradRudolph 我们针对四种情况(两个都为 null,两个都不为 null,只有一个为 null,只有两个为 null)有不同的返回值,所以我不确定你为什么说我们“真正关心的是其中一个或两个是否为 null,或者都不是”。 - Eran
@Eran 是的,返回值稍微有些让人困惑,但我认为这要么是个不好的例子,要么就是一些可疑的设计。无论如何,null几乎总是应该被特别处理,与实际逻辑分开来看。事实上,不这样做本身就是一个不好的设计,因为它会导致null泛滥成灾,而相反的情况则应该是真正的情况。 - Konrad Rudolph
1
你可以在前两个例子中都省略 else。例如:if(one==null){ return foo; } return bar; - Michael

25

由于函数的实际目的似乎是通过匹配非null对象来处理它们,因此我将在开头使用一个守卫语句处理所有null检查。

然后,一旦您确认没有参数为null,您可以处理实际的逻辑:

private int similarityCount(String a, String b) {
    if (a == null || b == null) {
        return a == b ? 1 : a == null ? 2 : 3;
    }

    return isMatch(a, b) ? 4 : 5;
}

这种方式比其他选项更为简洁易读。

话虽如此,真实的函数通常不会返回这样的数字代码。除非你的方法是为了举例说明问题而简化的,否则我强烈建议您重新考虑逻辑,并编写类似于以下内容的代码:

private boolean similarityCount(String a, String b) {
    if (a == null || b == null) {
        throw new NullPointerException();
    }

    return isMatch(a, b);
}
或者:
private boolean similarityCount(String a, String b) {
    if (a == null) {
        throw new IllegalArgumentException("a");
    }
    if (b == null) {
        throw new IllegalArgumentException("b");
    }

    return isMatch(a, b);
}

这些方法更为常规。然而,它们可能会触发异常。我们可以通过在Java 8中返回java.util.Optional<Boolean>来避免此情况:

private Optional<Boolean> similarityCount(String a, String b) {
    if (a == null || b == null) {
        return Optional.empty();
    }

    return Optional.of(isMatch(a, b));
}

乍一看,这似乎不比返回null更好,但是Optionals实际上要优越得多


很棒的答案,总是在寻求改进代码之前先查看该解决方案是个好主意。 - Viktor Mellgren

9

这段代码对我来说已经很清晰了。您可以使用嵌套和三元运算符使其更短:

if(one==null) {
    return two==null ? 1 : 2;
}
if(two==null) {
    return 3;
} 
return isMatch(one,two) ? 4 : 5;

1
我不确定短代码是否比更易读的代码更好。 如果没有任何if被满足,我会保留四个if而不是使用else和在块末尾添加return 5; - RadekJ
1
@RadekJ 我同意更易读的版本更好。一行上的三元运算符并没有真正降低可读性。在我看来,守卫语句更容易阅读。不过请发表你的版本,我会点赞的 :) - default locale
@RadekJ,实际上,Manh Le的答案非常接近您的描述。 - default locale
1
是的,我同意,我会写类似于Manh Le的代码,但你的代码是一个好的折衷方案 :) - RadekJ

5

可以使用Java条件运算符在一行中完成:

return (one==null?(two==null?1:2):(two==null?3:(isMatch(one,two)?4:5)));

15
仅仅因为一件事情“可以”做到,并不意味着它应该被做到 :) - DaveyDaveDave
1
我认为用户在询问更简短的方法。 :) 所以不知道为什么会被踩。 - SachinSarawgi
@SachinSarawgi 我给你点赞,因为没有任何理由去踩你。 - PSo
1
你的答案确实不正确,但是思路很好,不错。 - PSo
这段代码仍然与问题中的代码不等价。 - default locale
显示剩余2条评论

5

您可以创建一个伪查找表。有些人不喜欢嵌套的三元运算符,并且高度依赖空格来提高可读性,但它可以是一种非常易读的条件返回方法:

private int similarityCount (String one, String two) {
    return (one == null && two == null) ? 1
         : (one == null && two != null) ? 2
         : (one != null && two == null) ? 3
         : isMatch(one, two)            ? 4
         :                                5;
}

1

我喜欢表达式。

private static int similarityCount (String one, String two) {    
    return one == null ? 
        similarityCountByTwoOnly(two) : 
        two == null ? 3 : (isMatch(one, two) ? 4 : 5)
    ;
}

private static int similarityCountByTwoOnly(String two) {
    return two == null ? 1 : 2;
}

作为旁注,我可能会质疑你为什么要这样做。我会假设在评估返回的整数后,您会对其进行某种检查,并基于此分支您的逻辑。如果是这样的话,您刚刚进行了一项不太易读的空值检查,方法使用者需要理解整数值中隐含的契约。
另外,当您需要检查字符串是否相等但它们可能为空时,以下是一个简单的解决方案:
boolean same = one == null ? two == null : one.equals(two);

0

摆脱 IF 语句是一件有趣的事情。使用 Map 是其中一种方法。由于调用了 isMatch 方法,它并不完全适用于这种情况,但我提供它作为一个替代方案,可以将 similarityCount 方法体缩减为一行代码和一个 IF。

以下代码有两个 IF。如果 GetOrDefault 没有评估第二个参数,它可以被缩减为一个 IF。不幸的是,它确实这样做了,因此 isMatch 内部的空值检查是必要的。

如果您愿意,您可以进一步简化这个过程。例如,isMatch 可以返回 4 或 5 而不是布尔值,这将帮助您进一步简化。

import com.google.common.collect.ImmutableMap;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;

import java.util.Map;

public class SimilarityCount {

    private Map<SimilarityCountKey, Integer> rtn = ImmutableMap.of(new SimilarityCountKey(null, null), 1, new SimilarityCountKey(null, ""), 2, new SimilarityCountKey("", null), 3);

    public int similarityCount(String one, String two) {
        return rtn.getOrDefault(new SimilarityCountKey(one, two), isMatch(one, two) ? 4 : 5);
    }

    private boolean isMatch(String one, String two) {
        if (one == null || two == null) {
            return false;
        }
        return one.equals(two);
    }

    private class SimilarityCountKey {
        private final boolean one;
        private final boolean two;

        public SimilarityCountKey(String one, String two) {
            this.one = one == null;
            this.two = two == null;
        }

        @Override
        public boolean equals(Object obj) {
            return EqualsBuilder.reflectionEquals(this, obj);
        }

        @Override
        public int hashCode() {
            return HashCodeBuilder.reflectionHashCode(this);
        }
    }
}

如果有其他人想尝试另一种解决方案,这里有一些测试可以帮助你入手

 import org.junit.Assert;
import org.junit.Test;

import static org.hamcrest.CoreMatchers.is;

public class SimilarityCountTest {

    @Test
    public void one(){
        Assert.assertThat(new SimilarityCount().similarityCount(null,null), is(1));
    }

    @Test
    public void two(){
        Assert.assertThat(new SimilarityCount().similarityCount(null,""), is(2));
    }

    @Test
    public void three(){
        Assert.assertThat(new SimilarityCount().similarityCount("",null), is(3));
    }

    @Test
    public void four(){
        Assert.assertThat(new SimilarityCount().similarityCount("",""), is(4));
    }

    @Test
    public void five(){
        Assert.assertThat(new SimilarityCount().similarityCount("a","b"), is(5));
    }

}

每一行代码都是一份负债。此外,KISS和YAGNI原则同样适用。 - Erik Madsen
这个解决方案的圈复杂度要低得多 - 2个if语句 vs(至少)五个,具体取决于isMatch如何实现。我同意这不是一个适用于此情况的整洁解决方案,但对于许多if/switch语句,这种方法可以大大降低复杂性。 - Mark Chorley
我看不出来YAGNI如何适用。就我所见,所有的代码都在使用中。 - Mark Chorley
如果我正确理解了你的论点,你选择了使用 Map 来达到将来的可扩展性。而对此的反驳是 YAGNI。 - Erik Madsen

0

如果两者都不为空,那么这种方法应该会稍微快一些,因为在这种情况下只执行一个“if”语句。

private int similarityCount (String one, String two) {

    if (one == null || two == null) {  // Something is null
        if (two != null) { // Only one is null
            return 2;
        }

        if (one != null) { // Only two is null
            return 3;
        }

        return 1; // Both must be null
    } 

    return isMatch(one, two) ? 4 : 5;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接