三元表达式中带有自动装箱的 NullPointerException

发布于 2024-09-10 15:48:21 字数 171 浏览 6 评论 0原文

运行以下Java代码:

boolean b = false;
Double d1 = 0d;
Double d2 = null;
Double d = b ? d1.doubleValue() : d2;

为什么会出现NullPointerException?

Run the following Java code:

boolean b = false;
Double d1 = 0d;
Double d2 = null;
Double d = b ? d1.doubleValue() : d2;

Why is there a NullPointerException?

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

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

发布评论

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

评论(4

凉栀 2024-09-17 15:48:21

条件表达式 b ? 的返回类型d1.doubleValue : d2double。条件表达式必须具有单一返回类型。遵循二进制数字提升的规则,d2 会自动拆箱为 double,这会在 d2 == null 时导致 NullPointerException代码>.

根据语言规范第 §15.25 节:

否则,如果第二个和第三个
操作数的类型是
可转换(第 5.1.8 节)为数字类型,
那么有几种情况:...

否则,将应用二进制数字提升 (§5.6.2)
操作数类型和类型
条件表达式是
第二、第三种提升型
操作数。请注意,二进制数字
促销执行拆箱转化
(§5.1.8) 和值集转换
(§5.1.13)。

The return type of the conditional expression b ? d1.doubleValue : d2 is double. A conditional expression must have a single return type. Following the rules for binary numeric promotion, d2 is autounboxed to a double, which causes a NullPointerException when d2 == null.

From the language spec, section §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).

岁月如刀 2024-09-17 15:48:21

因为 : 周围的两个表达式必须返回相同的类型。这意味着 Java 尝试将表达式 d2 转换为 double。这意味着字节码在 d2 上调用 doubleValue() -> NPE。

Because the two expressions around : must return the same type. This means Java tries to convert the expression d2 to double. This means the bytecode calls doubleValue() on d2 -> NPE.

海夕 2024-09-17 15:48:21

通常应该避免混合类型计算;将其与 ?: 条件/三元复合只会使情况变得更糟。

以下引用自 Java Puzzlers,《Puzzle 8:Dos Equis》:

混合类型计算可能会令人困惑。这一点比条件表达式更明显。 [...]

确定条件表达式结果类型的规则太长且复杂,无法完整重现,但这里有三个关键点。

  1. 如果第二个和第三个操作数具有相同的类型,即为条件表达式的类型。换句话说,您可以通过避开混合类型计算来避免整个混乱。

  2. 如果其中一个操作数的类型为 T,其中 Tbyteshort,或 char,另一个操作数是 int 类型的常量表达式,其值可以用 T 类型表示,条件表达式的类型为T

  3. 否则,将对操作数类型应用二进制数值提升,并且条件表达式的类型是第二个和第三个操作数的提升类型。

这里应用了第3点,结果就是拆箱。当您取消装箱 null 时,自然会抛出 NullPointerException

这是混合类型计算和 ?: 的另一个示例,这可能会令人惊讶:

    Number n = true ? Integer.valueOf(1) : Double.valueOf(2);

    System.out.println(n); // "1.0"
    System.out.println(n instanceof Integer); // "false"
    System.out.println(n instanceof Double);  // "true"

混合类型计算是至少 3 个 Java Puzzlers 的主题。

最后,这是 Java Puzzlers 的规定:

4.1。混合类型计算令人困惑

处方:避免混合类型计算。

?: 运算符与数字操作数一起使用时,请对第二个和第三个操作数使用相同的数字类型。


关于首选基本类型胜过装箱基元

以下引用来自《Effective Java 第二版》第 49 条:首选基本类型胜过装箱基元:

总之,只要有选择,就优先使用基元而不是盒装基元。原始类型更简单、更快。如果您必须使用盒装原语,请小心!自动装箱减少了使用装箱原语的冗长性,但并没有减少危险。当您的程序使用 == 运算符比较两个装箱基元时,它会进行身份比较,这几乎肯定不是您想要的。当您的程序执行涉及装箱和拆箱基元的混合类型计算时,它会进行拆箱,并且当您的程序进行拆箱时,它可能会抛出 NullPointerException。最后,当您的程序装箱原始值时,可能会导致昂贵且不必要的对象创建。

在某些地方,您别无选择,只能使用装箱基元,例如泛型,但除此之外,您应该认真考虑使用装箱基元的决定是否合理。

相关问题

You should generally avoid mixed type computation; compounding this with ?: conditional/ternary only makes it worse.

Here's a quote from Java Puzzlers, Puzzle 8: Dos Equis:

Mixed-type computation can be confusing. Nowhere is this more apparent than conditional expression. [...]

The rules for determining the result type of a conditional expression are too long and complex to reproduce in their entirety, but here are three key points.

  1. If the second and third operands have the same type, that is the type of the conditional expression. In other words, you can avoid the whole mess by steering clear of mixed-type computation.

  2. If one of the operands is of type T where T is byte, short, or char, and the other operand is a constant expression of type int whose value is representible in type T, the type of the conditional expression is T.

  3. Otherwise, binary numeric promotion is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands.

Point 3 is applied here, and it resulted in unboxing. When you unbox null, naturally a NullPointerException is thrown.

Here's another example of mixed-type computation and ?: that may be surprising:

    Number n = true ? Integer.valueOf(1) : Double.valueOf(2);

    System.out.println(n); // "1.0"
    System.out.println(n instanceof Integer); // "false"
    System.out.println(n instanceof Double);  // "true"

Mixed-type computation is the subject of at least 3 Java Puzzlers.

In closing, here's what Java Puzzlers prescribes:

4.1. Mixed-type computations are confusing

Prescription: Avoid mixed-type computations.

When using the ?: operator with numeric operands, use the same numeric type for both the second and third operands.


On prefering primitive types to boxed primitives

Here's a quote from Effective Java 2nd Edition, Item 49: Prefer primitive types to boxed primitives:

In summary, use primitives in preference to boxed primitive whenever you have the choice. Primitive types are simpler and faster. If you must use boxed primitives, be careful! Autoboxing reduces the verbosity, but not the danger, of using boxed primitives. When your program compares two boxed primitives with the == operator, it does an identity comparison, which is almost certainly not what you want. When your program does mixed-type computations involving boxed and unboxed primitives, it does unboxing, and when your program does unboxing, it can throw NullPointerException. Finally, when your program boxes primitive values, it can result in costly and unnecessary object creations.

There are places where you have no choice but to use boxed primitives, e.g. generics, but otherwise you should seriously consider if a decision to use boxed primitives is justified.

Related questions

最美不过初阳 2024-09-17 15:48:21

对于如下所示的两种条件返回相同的类型,您将得到结果。

boolean b = false;
Double d1 = 0d;
Double d2 = null;
Double d = b ? d1 : (Double)d2;
System.out.println(d);

Return same type for both condition like below and you will get result.

boolean b = false;
Double d1 = 0d;
Double d2 = null;
Double d = b ? d1 : (Double)d2;
System.out.println(d);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文