三元运算符在条件未满足时评估条件语句

时间:2014-05-02 14:45:03

标签: java nullpointerexception ternary-operator

我写了一些代码;这里有相关的片段:

@NonNullByDefault
public class Score<NUMERAL, LITERAL>
{
    protected NUMERAL value;
    @Nullable
    protected LITERAL literal;
    [...]

我已覆盖我的equals()方法,如下所示:

@Override
public boolean equals(@Nullable Object object)
{
    if(object == null) return false;
    if(object == this) return true;

    if( object instanceof Score)
    {
        return ((Score<NUMERAL, LITERAL>) object).getValue().equals(value) &&
                literal == null ? ((Score<NUMERAL, LITERAL>) object).getLiteral() == null : literal.equals(((Score<NUMERAL, LITERAL>) object).getLiteral());
    }
    return false;
}

基本上,我们的想法是Score可能只有一个数值,在这种情况下literal为空。我编写了一些单元测试,并使用下面的代码获取空指针异常:

[....]
Score<Float, String> score = new Score<>(0.0f);
Score<Float, String> anotherScore = new Score<>(1.0f, "One");
[....]

assertFalse(score.equals(anotherScore));

如果我没有记错的话,equals中的短切是否应该在执行&&之后阻止任何事情,因为第一个表达式已经false?此外,为什么例外?由于条件为真,我希望评估三元的表达式并跳过条件表达式。从我在规范中读到的内容来看,这应该是行为。此外,我发现了这个问题:Java ternary (immediate if) evaluation这应该为我的思考过程带来更多的影响。

也许我忽略了一些相当明显的东西,但我没有想法。也许你可以帮忙吗?

1 个答案:

答案 0 :(得分:3)

它短路正常,但不是你想要它的方式。 &&的优先级高于三元?: - 因此(缩进,换行符和注释添加以澄清)

((Score<NUMERAL, LITERAL>) object).getValue().equals(value) &&
literal == null
    ? ((Score<NUMERAL, LITERAL>) object).getLiteral() == null
    : literal.equals(((Score<NUMERAL, LITERAL>) object).getLiteral())

实际上意味着:

//the first line as a whole is the condition for ?:
((Score<NUMERAL, LITERAL>) object).getValue().equals(value) && literal == null
    ? ((Score<NUMERAL, LITERAL>) object).getLiteral() == null
    : literal.equals(((Score<NUMERAL, LITERAL>) object).getLiteral())

这意味着,在实践中,如果条件的第一部分是falseliteralnull,则会自动输入表达式的:部分您致电literal.equals,导致NullPointerException

修复很简单:添加括号以告诉Java您希望评估内容的方式:

((Score<NUMERAL, LITERAL>) object).getValue().equals(value) && 
(literal == null 
    ? ((Score<NUMERAL, LITERAL>) object).getLiteral() == null
    : literal.equals(((Score<NUMERAL, LITERAL>) object).getLiteral()))