在Java的三元运算符中,即使表达式导致错误值,也可以评估第一个参数吗?

时间:2011-02-07 16:24:37

标签: java ternary-operator

我最近通过随机ad-hoc测试在我的代码中发现了一个不寻常的错误。所以,我为它做了一个测试用例。

这是我的测试用例:

 SampleRequest request = new SampleRequest();
    request.setA(null);
    request.setB(null);
    assertEquals(null, request.getAOrB());

A和B被定义为java.lang.Integer类型,并有直接的setter方法将它们的值设置为请求。

还涉及一个枚举。它有一个原始整数值,以及在此代码中使用的方法。我会在这里发布相关部分:

enum Swapper {
public int c;
Swapper findSwapperToUse(final int a) {
   for(Swapper swapper : values()) {
       if(swapper.c == a) {
          return swapper;
       }
   }
   return null;
}
}

现在,这里的方法令人困惑。在该方法上调用测试方法会导致NPE,但是在方法的最后一行。

    public class SampleRequest {
    private Integer A;
    private Integer B;

    public void setA(final Integer A) {
        this.A = A;
    }

    public void setB(final Integer B) {
        this.B = B;
    }


public Integer getAOrB() {
    return A != null ? Swapper.findSwapperToUse(A).c
         : B;
}
}

在测试中,A和B都设置为null。因此,A!= null返回false。但是,我在:B行的行号中得到NullPointerException。

我的猜测是由于某种原因,正在评估第一个表达式Swapper.findSwapperToUse(A).c,因此通过自动装箱调用A.intValue(),导致null值出现NullPointerException。通过调试,已知不会调用findSwapperToUse()。

然而,根据这个问题,这不应该发生: Java ternary (immediate if) evaluation

  

未针对条件表达式的特定评估评估未选择的操作数表达式。

返回null(B)不会导致NullPointerException - 在这里返回null结果完全没问题。

到底发生了什么事?

编辑:我忘了补充说我改变了代码以通过使用直接if语句来避免这种情况 - 以下代码确实按预期工作:

public Integer getAOrB() {
    if(A != null) {
        return Swapper.findSwapperToUse(A).c;
    }
    return B;
}

2 个答案:

答案 0 :(得分:22)

我猜这个问题是由编译器推断整个表达式的类型

引起的
A != null ? Swapper.findSwapperToUse(A).c : B

来自int类型的Swapper.c,因此会尝试将取消装箱转化应用于B

以下是the JLS, §15.25的相关摘录:

  
      
  • 否则,如果第二个和第三个操作数具有可转换的类型   (§5.1.8)数字类型,则有几种情况:
      
        
    • ...
    •   
    • 否则,二进制数字提升(第5.6.2节)将应用于操作数   类型,条件表达式的类型是提升类型   第二和第三个操作数。 请注意二进制数字促销执行   拆箱转换(§5.1.8)和价值集转换(§5.1.13)。
    •   
  •   

您可以通过添加以下演员来阻止它:

A != null ? (Integer) Swapper.findSwapperToUse(A).c : B

答案 1 :(得分:0)

您的findSwapperToUse方法返回null,您无法执行null.c

为确保这一点,我会将您的代码更改为:

public Integer getAOrB() {
    if(A != null) {
        Swapper foundSwapper = Swapper.findSwapperToUse(A);
        return foundSwapper.c;
    }
    return B;
}