13

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

这是我的测试用例:

 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 都设置为空。因此,A != null 返回 false。但是,我在 : B 行的行号处收到 NullPointerException。

我的猜测是,由于某种原因,正在评估第一个表达式 Swapper.findSwapperToUse(A).c,因此 A.intValue() 通过自动装箱调用,导致空值出现 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;
}
4

2 回答 2

22

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

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

从的int类型开始Swapper.c,因此尝试将拆箱转换应用于B.

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

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

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

A != null ? (Integer) Swapper.findSwapperToUse(A).c : B
于 2011-02-07T16:41:18.993 回答
0

你的findSwapperToUse方法返回null,你不能这样做null.c

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

public Integer getAOrB() {
    if(A != null) {
        Swapper foundSwapper = Swapper.findSwapperToUse(A);
        return foundSwapper.c;
    }
    return B;
}
于 2011-02-07T16:29:57.607 回答