我最近通过随机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;
}
答案 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;
}