在Java中';s的三元运算符,即使表达式产生假值,也可以计算第一个参数吗?

在Java中';s的三元运算符,即使表达式产生假值,也可以计算第一个参数吗?,java,ternary-operator,Java,Ternary Operator,我最近通过随机的特别测试在我的代码中发现了一个不寻常的错误。所以,我为它做了一个测试用例 以下是我的测试用例: SampleRequest request = new SampleRequest(); request.setA(null); request.setB(null); assertEquals(null, request.getAOrB()); A和B被定义为java.lang.Integer类型,并有直接的setter方法将它们的值设置到请求中 还涉及到

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

以下是我的测试用例:

 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()未被调用

然而,根据这一问题,这不应该发生:

未选择的操作数表达式不会针对条件表达式的特定计算进行计算

返回null(B)不会导致NullPointerException-在这里返回null结果完全可以

到底发生了什么事

编辑:我忘了补充一点,我通过使用一个直接的if语句修改了代码以避免出现这种情况-以下代码按预期工作:

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

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

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

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

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

A != null ? Swapper.findSwapperToUse(A).c : B
作为
Swapper.c
类型的
int
,因此尝试将取消装箱转换应用于
B

以下是相关摘录:

  • 否则,如果第二个和第三个操作数具有可转换的类型 (§5.1.8)到数字类型,则有几种情况:
    • 否则,对操作数应用二进制数字提升(§5.6.2) 类型,条件表达式的类型是 第二和第三个操作数请注意,二进制数字升级执行 拆箱转换(§5.1.8)和值集转换(§5.1.13)
您可以通过添加以下强制类型转换来防止它:

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

您给出的代码无法编译(例如,缺少返回类型)。请给出一个简短但完整的示例,我们来看看发生了什么。您如何能够绝对确定a为null?我提供了返回类型,抱歉-它是Swapper@Thor-由于测试(request.setA(null)、request.setB(null))@MetroidFan,仍然需要一个完整的(可编译的)示例。我无法仅使用代码的相关部分复制它。可能是因为Java版本的差异,但也可能是因为bug没有达到您的预期。例如,由于某些复制粘贴,setA()和setB()实际上都设置了B的值。这与自动装箱无关,是吗?虚拟机试图将空值转换为整数?我有点抓紧救命稻草,因为我在你的代码中看不到任何其他东西,但我想我会把它扔进锅里!我也是这么想的,但这并不能解释问题的底部。OP明确表示这个方法没有被调用。考虑到A是空的,所以它不可能是接受
int
@Sergey-oops的函数的参数,这一点也不奇怪:)这很荒谬,但事实就是如此!谢谢。:(应该有一个编译器标志来禁用自动装箱来捕获这些内容。@MetroidFan,有趣的是,你的堆栈跟踪实际上指向了正确的行。当我在我的电脑上复制它时,我在
a!=null
行得到了NPE。我想它只是指向了它发生的语句,而不是实际的行。