在 Java 的三元运算符中,即使表达式结果为假值,也可以计算第一个参数吗?

发布于 2024-10-16 16:04:35 字数 1689 浏览 3 评论 0原文

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

这是我的测试用例:

 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.findSwaperToUse(A).c,因此通过自动装箱调用 A.intValue(),从而导致 null 值出现 NullPointerException。通过调试发现findSwaperToUse()没有被调用。

然而,根据这个问题,这不应该发生: Java 三元(立即 if)评估

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

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

到底是怎么回事?

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

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

I found an unusual bug in my code recently through random ad-hoc testing. So, I made a test case for it.

Here is my test case:

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

A and B are defined as java.lang.Integer types and have direct setter methods to set their values into the request.

There is also an enumeration involved. It has a primitive integer value, and a method that's used in this code. I'll post the relevant portions here:

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

Now, here's the method that is being confusing. Invoking the test method on that method results in a NPE, but on the last line of the method.

    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;
}
}

In the test, both A and B are set to null. Therefore, A != null returns false. However, I get a NullPointerException at the line number for the : B line.

My guess is that for some reason the first expression, Swapper.findSwapperToUse(A).c, is being evaluated, and therefore the A.intValue() is invoked via autoboxing, resulting in a NullPointerException on the null value. Through debugging, it's known that findSwapperToUse() is not invoked.

However, according to this questionthis should not happen:
Java ternary (immediate if) evaluation

The operand expression not chosen is not evaluated for that particular evaluation of the conditional expression.

Returning a null (B) will not result in a NullPointerException - it's perfectly fine to return a null result here.

What the heck is going on?

EDIT: I forgot to add that I altered the code to avoid this by using a straight up if statement - the following code does work as expected:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

嘿咻 2024-10-23 16:04:35

我猜问题是由于编译器

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

Swapper.c 的类型推断出整个表达式的类型为 int ,因此尝试将拆箱转换应用于B

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

  • 否则,如果第二个和第三个操作数具有可转换的类型
    (§5.1.8)改为数值类型,那么有几种情况:

    • ...
    • 否则,二进制数字提升 (§5.6.2) 将应用于操作数
      类型,并且条件表达式的类型是该类型的提升类型
      第二和第三操作数。 请注意,二进制数字提升执行
      拆箱转换(§5.1.8)
      和值集转换(§5.1.13)。

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

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

I guess the problem is caused by the fact that compiler infers the type of the whole expression

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

as int from the type of Swapper.c, and therefore tries to apply unboxing conversion to B.

Here is the related excerpt from the JLS, §15.25:

  • Otherwise, if the second and third operands have types that are convertible
    (§5.1.8) to numeric types, then there are several cases:

    • ...
    • Otherwise, binary numeric promotion (§5.6.2) is applied to the operand
      types, and the type of the conditional expression is the promoted type of the
      second and third operands. Note that binary numeric promotion performs
      unboxing conversion (§5.1.8)
      and value set conversion (§5.1.13).

You can prevent it by adding the following cast:

A != null ? (Integer) Swapper.findSwapperToUse(A).c : B
孤独难免 2024-10-23 16:04:35

您的 findSwaperToUse 方法返回 null,并且您无法执行 null.c

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

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

Your findSwapperToUse method is returning null, and you can't do null.c.

To make sure of this, I would change your code to read:

public Integer getAOrB() {
    if(A != null) {
        Swapper foundSwapper = Swapper.findSwapperToUse(A);
        return foundSwapper.c;
    }
    return B;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文