7

参考:http: //java.sun.com/j2se/1.5.0/docs/guide/language/autoboxing.html

“如果你的程序试图自动拆箱 null,它会抛出 NullPointerException。”

如果您尝试将 null 分配给布尔值,javac 会给您一个编译时错误。说得通。不过,将 null 分配给布尔值是可以的。也有道理,我猜。

但是让我们考虑一下在尝试自动拆箱 null 时会得到 NPE 的事实。这意味着您不能在没有空值检查或异常处理的情况下安全地对布尔值执行布尔运算。对整数进行数学运算也是如此。

很长一段时间以来,我都是 java1.5+ 中自动装箱的粉丝,因为我认为它让 java 更接近真正的面向对象。但是,在昨晚遇到这个问题之后,我得说我觉得这很糟糕。当我尝试使用未初始化的原语做一些事情时,编译器给我一个错误是一件好事。如果我丢失了,我不想使用自动装箱。

我想我可能误解了自动装箱的意义,但同时我永远不会接受布尔值应该能够有 3 个值。谁能解释一下?我没有得到什么?

4

9 回答 9

16

盒装类型是引用类型,所有引用类型,无论是否为原始盒,都可以引用null. 这就是为什么 aBoolean可以引用null. 一个Integer. aString等也可以。

盒装类型并非旨在使 Java 真正面向对象。Java永远不会是一种纯粹的面向对象的语言,你不应该像这样编写代码。原始类型永远不会消失,事实上,只要有选择,就应该首选。

这是来自Effective Java 2nd Edition, Item 49: Prefer primitive types to boxed primitives的引述(作者强调):

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

于 2010-05-27T17:21:04.547 回答
7

我至少见过一种null价值有用的案例。Web 服务中的许多数据对象都有可为空的布尔字段。有时用户会忽略包含一个值。在这种情况下,您希望能够从默认值中辨别出缺少值。以前,人们会编写getXsetXisXSet()方法,其中isXSet返回 false 直到有人调用setX。现在可以将其设为X可为空的类型,并且很明显它没有设置 if getXreturns null

于 2010-05-27T17:28:30.473 回答
5

除了这里所说的一切之外,在某些情况下,您非常希望为布尔值设置第三个值——“可选”属性的情况。

我们通常在使用允许空值的布尔列的数据库中遇到它。如果不使用布尔值,我们将需要使用两个单独的变量,一个表示值,另一个表示它是否有效。

事实上,如果你看一下 JDBC API,你可以看到这个问题的一个例子,如果列为空(例如,数字字段为 0),则列获得默认值,然后你必须调用“wasNull”来检查它是真 0 还是假 null!

于 2010-05-27T17:24:45.490 回答
4

您的问题是自动取消装箱,而不是自动装箱。我认为自动拆箱是邪恶的,原因更重要:

考虑一下:

Integer i = new Integer(1);
Integer i2 = new Integer(12);

System.out.println(i == 10 || i != i2);

一个==拆箱,另一个不拆箱。

在我看来,对运算符(而不是赋值)进行拆箱是一个错误(鉴于上述情况——它只是不是 Java)。然而,拳击是非常好的。

于 2010-05-27T17:31:52.850 回答
1

我认为这比技术问题更具哲学性。当您将原始类型转换为引用类型时,您应该准备好引用类型(即对象)可以为空。

您可以观看The Billion Dollars Mistake演示文稿,其中 CAR Hoare 说他引入对 oop (Algol 60) 的空引用是一个错误。

Effective Java 中的 Josh Bloch 建议尽可能使用原始类型。但有时您确实必须针对 null 验证您的布尔变量。

于 2010-05-27T17:21:23.147 回答
0

自动装箱会自动在内部类型和对象类型之间转换数据类型。

Boolean 的对象类型可以是Boolean.TRUEBoolean.FALSEnull。这就是为什么您必须处理布尔值可能的 3 个值。

在数据库中,布尔类型通常具有三种状态。考虑一个跟踪某人是否通过课程的记录。有TRUE,传球;FALSE 表示未通过,NULL 表示“当前正在上课”。是的,这很奇怪,但在面向对象编程中没有值是继承的。

我也觉得自动装箱有点令人反感,主要是因为它是编译器添加字节码来处理转换过程的功能。在我看来,这可能会导致人们忘记转换过程中可能更好记住的重要细节(例如您的案例中的 null 处理)。它很有用,但不如 Java 语言的大多数其他特性有用。

就个人而言,我希望内在函数被实现为轻量级对象而不是“内置”类型。很多时候,混合内在/对象类型系统会阻碍。也就是说,内在函数应该可以提高性能,但似乎如果您必须对对象编组执行大量内在函数,您将无法享受仅内在函数的性能提升。

于 2010-05-27T17:20:27.580 回答
0

实际上与 Java 1.5 之前的日子没有太大区别——问题不是布尔类型(它仍然有两个状态),而是布尔包装器(它总是有 3 个状态。Boolean.TRUE、Boolean.FALSE 和 null)。

从布尔对象到布尔原语的每次转换都需要空检查,无论是否自动装箱。

于 2010-05-27T17:22:00.413 回答
0

这是自动装箱的问题,就像Integer i = null;. Integerobject 可以为 null,而 nativeint不能。

于 2010-05-27T17:31:33.433 回答
0

随着自动装箱的引入,它从未打算取代原语。在大多数地方,原语就是你想要的。如果您想开始使用引用类型,因为“它使 java 更接近于真正面向对象”,那么这就是您的要求,但正如您所发现的那样,这种方法存在缺点。性能将是另一个问题。

自动装箱(和自动拆箱)有助于在使用原语的代码(大多数)和必须使用引用类型的代码(很少见)之间进行转换。在引用类型中,Boolean 无疑是最稀有的,因为它的实例数量很少意味着它几乎不值得放入 Collection。

总之:如果引用类型给您带来麻烦,请不要使用它们。但是不要仅仅因为它们对您的情况没有帮助就说“自动装箱不好”。

于 2010-05-27T17:34:36.107 回答