我应该不再使用原始类型吗?

时间:2020-03-06 14:37:48  来源:igfitidea点击:

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

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

稍后,我们将希望定义或者未定义变量i,因此将上述实例更改为:

Integer i = null;

现在,相等性检查失败。

始终使用原始包装器类是Java的好习惯吗?显然,这样做会尽早消除一些错误,但是这样做的不利之处是什么?它会影响性能或者应用程序的内存占用吗?有偷偷摸摸的陷阱吗?

解决方案

首先,从使用原语切换到仅使用对象以获得将其设置为null的能力可能是一个错误的设计决策。我经常与我的同事争论是否null是一个前哨值,我通常认为它不是(因此不应像前哨值那样被禁止),但是在这种情况下不适合将其用作哨兵值。请不要。创建一个指示布尔值是否有效的布尔值,或者创建一个将布尔值和整数包装在一起的新类型。

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

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

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

重新存储时,对象必须存储在堆中(这也会影响性能),还必须存储值本身。

偷偷摸摸的陷阱?当我为null时,i.equals(j)。

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

那里的Java POD类型是有原因的。除了开销之外,我们无法对对象执行常规操作。整数是一个对象,需要对其进行分配和垃圾回收。一个int不是。

在示例中,if语句在我们超过127之前都是可以的(因为Integer自动装箱将缓存最多127个值,并为每个数字返回相同的实例,直到此值)

所以这比你提出来的还要糟糕...

if( i == 10 )

会像以前一样工作,但是

if( i == 128 )

将失败。出于这样的原因,我总是在需要时显式创建对象,并且在可能的情况下倾向于使用原始变量

我建议我们始终使用原语,除非我们真的有" null"的概念。

是的,VM会执行自动装箱以及所有这些操作,但是这可能会导致一些非常奇怪的情况,在这些行中,我们会在真正不希望的代码行中得到空指针异常,因此必须开始执行空检查在每个数学运算上。如果我们开始混合类型并获得奇怪的自动装箱行为,那么我们还可以开始获得一些非显而易见的行为。

对于float / double,我们可以将NaN视为null,但请记住NaN!= NaN,因此我们仍需要特殊检查,例如!Float.isNaN(x)。

如果有支持原始类型的集合,而不必浪费时间/拳击费用,那将是非常好的。

如果该值可以为空,则我们可能会发现在设计中需要其他功能。

有两种可能-值仅仅是数据(无论是否填写,代码的行为都不会改变),或者实际上表明我们在这里有两种不同类型的对象(如果存在,代码的行为会不同一个比空值大的值)

如果仅是用于显示/存储的数据,则可以考虑使用真正的DTO-完全没有DTO作为一流成员。那些通常将具有一种检查是否已设置值的方法。

如果在某个时候检查null,则可能要使用子类,因为当存在一个差异时,通常会有更多差异。至少我们希望有一种更好的方式来表明区别,而不是" if nativeIntValue == null",那实际上没有任何意义。

不要仅仅为了获得此功能就切换到非基本体。使用布尔值指示是否设置了值。如果我们不喜欢该解决方案,并且知道整数将处于某个合理的范围内(或者不在乎偶然的失败),请使用特定的值表示"未初始化",例如Integer.MIN_VALUE。但这是一个比布尔型安全得多的解决方案。

当我们到达"稍后"点时,在重构过程中还需要完成一些工作。尽可能使用原语。 (大写字母)如果需要更多功能,则进行POJO。在我看来,原始包装器类最适合用于需要在线传输的数据,这意味着联网的应用程序。将null设置为可接受的值会导致系统"增长",令人头疼。对于很多浪费或者遗漏的代码,请注意应该进行简单的比较。