16

在 Java 中混合使用原始数据类型及其各自的包装类可能会导致很多错误。以下示例说明了该问题:

int i = 4;
...
if (i == 10)
  doStuff();

稍后您发现您希望变量i被定义或未定义,因此您将上述实例更改为:

Integer i = null;

现在相等检查失败。

始终使用原始包装类是一种好的 Java 实践吗?显然它会尽早解决一些错误,但是这样做的缺点是什么?它会影响性能或应用程序的内存占用吗?有什么偷偷摸摸的陷阱吗?

4

8 回答 8

18

使用盒装类型确实存在性能和内存问题。

在进行比较时(例如(i == 10)),java 必须在进行比较之前对类型进行拆箱。即使使用i.equals(TEN)方法调用,它也比 == 语法更昂贵且(IMO)丑陋。

重新记忆,对象必须存储在堆上(这也会影响性能)以及存储值本身。

一个偷偷摸摸的陷阱?i.equals(j)当我是null

我总是使用原语,除非它可能null,但null在这些情况下总是在比较之前检查。

于 2008-09-23T22:00:15.583 回答
8

首先,从使用原语切换到使用对象只是为了能够将其设置为空,这可能是一个糟糕的设计决策。我经常与我的同事争论 null 是否是哨兵值,我的意见通常是它不是(因此不应该像哨兵值那样被禁止),但在这种特殊情况下,你要去无法将其用作哨兵值。请不要。创建一个指示整数是否有效的布尔值,或者创建一个将布尔值和整数包装在一起的新类型。

通常,当使用较新版本的 Java 时,我发现我不需要显式创建或强制转换为原语的对象版本,因为在 1.5(可能是 1.5 本身)中添加了自动装箱支持。

于 2008-09-23T21:59:16.053 回答
3

我建议一直使用原语,除非你真的有“空”的概念。

是的,虚拟机现在做了自动装箱和所有这些,但它可能导致一些非常奇怪的情况,你会在你真的没想到的代码行中得到一个空指针异常,你必须开始做空检查在每一个数学运算上。如果您开始混合类型并获得奇怪的自动装箱行为,您也可以开始获得一些不明显的行为。

对于浮点数/双精度数,您可以将 NaN 视为 null,但请记住 NaN != NaN,因此您仍然需要像 !Float.isNaN(x) 这样的特殊检查。

如果有支持原始类型的集合而不是浪费时间/装箱开销,那就太好了。

于 2008-09-23T22:45:51.153 回答
1

在您的示例中, if 语句将正常,直到您超过 127(因为整数自动装箱将缓存高达 127 的值并为每个数字返回相同的实例,直到该值)

所以它比你呈现的更糟糕......

if( i == 10 )

会像以前一样工作,但是

if( i == 128 )

将失败。正是出于这样的原因,我总是在需要时明确地创建对象,并且如果可能的话,倾向于坚持使用原始变量

于 2008-09-23T22:01:45.580 回答
0

Java POD 类型的存在是有原因的。除了开销之外,您不能对对象进行正常操作。Integer 是一个对象,需要分配和垃圾收集。一个 int 不是。

于 2008-09-23T22:00:20.450 回答
0

如果该值可以为空,您可能会发现在您的设计中需要其他东西。

有两种可能性 - 值只是数据(无论是否填写,代码都不会有任何不同),或者它实际上表明您在这里有两种不同类型的对象(如果存在,则代码的行为会有所不同)一个值而不是空值)

如果它只是用于显示/存储的数据,您可能会考虑使用真正的 DTO——一个根本没有它作为一流成员的 DTO。这些通常有一种方法可以检查是否已设置值。

如果您在某个时候检查 null,您可能希望使用子类,因为当有一个差异时,通常会有更多。至少您想要一种比“如果primitiveIntValue == null”更好的方法来表明您的差异,这并不意味着什么。

于 2008-09-23T23:31:44.693 回答
0

不要仅仅为了获得这种便利而转向非原始人。使用布尔值来指示该值是否已设置。如果您不喜欢该解决方案并且您知道您的整数将处于某个合理的限制(或不关心偶尔的失败),请使用特定值来指示“未初始化”,例如 Integer.MIN_VALUE。但这是一个比布尔值安全得多的解决方案。

于 2008-09-24T17:37:26.690 回答
0

当您到达“稍后”时,需要在重构期间完成更多工作。尽可能使用原语。(资本期)如果需要更多功能,则制作 POJO。在我看来,原始包装类最适合用于需要通过网络传输的数据,即网络应用程序。随着系统“增长”,允许空值作为可接受的值会导致令人头疼的问题。太多代码被浪费或遗漏,以保护应该是简单的比较。

于 2008-09-25T03:36:55.313 回答