在Java的三元运算符中,即使表达式导致假值,是否可以计算第一个参数?

2022-09-04 08:38:42

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

这是我的测试用例:

 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()是通过自动装箱调用的,导致空值上的NullPointerException。通过调试,可以知道不会调用 findSwapperToUse()。

但是,根据这个问题,这不应该发生:Java三元(立即如果)评估

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

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

这到底是怎么回事?

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

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

答案 1

我想这个问题是由编译器推断整个表达式的类型这一事实引起的。

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

从 的类型中,因此尝试将取消装箱转换应用于 。intSwapper.cB

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

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

您可以通过添加以下强制转换来防止它:

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

答案 2

您的方法返回 null,并且不能执行 .findSwapperToUsenull.c

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

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