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

时间:2021-07-24 13:27:51

Mixing the use of primitive data types and their respective wrapper classes, in Java, can lead to a lot of bugs. The following example illustrates the issue:

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

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

Later on you figure that you want the variable i to be either defined or undefined, so you change the above instantiation to:

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

Integer i = null;

Now the equality check fails.

现在,相等检查失败。

Is it good Java practise to always use the primitive wrapper classes? It obviously would get some bugs out of the way early, but what are the downsides to this? Does it impact performance or the application's memory footprint? Are there any sneaky gotchas?

始终使用原始包装类是不是很好的Java实践?很明显,它会在早期出现一些错误,但这有什么缺点?它是否会影响性能或应用程序的内存占用?有没有偷偷摸摸的陷阱?

8 个解决方案

#1


18  

Using the boxed types does have both performance and memory issues.

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

When doing comparisons (eg (i == 10) ), java has to unbox the type before doing the comparison. Even using i.equals(TEN) uses a method call, which is costlier and (IMO) uglier than the == syntax.

在进行比较时(例如(i == 10)),java必须在进行比较之前取消打包类型。即使使用statesquals(TEN)也会使用方法调用,这种方法调用比==语法更昂贵且(IMO)更加丑陋。

Re memory, the object has to be stored on the heap (which also takes a hit on performance) as well as storing the value itself.

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

A sneaky gotcha? i.equals(j) when i is null.

鬼鬼祟祟的骗局?当我为空时,等于(j)。

I always use the primitives, except when it may be null, but always check for null before comparison in those cases.

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

#2


8  

Firstly, switching from using a primitive to using an object just to get the ability to set it to null is probably a bad design decision. I often have arguments with my coworkers about whether or not null is a sentinel value, and my opinion is usually that it is not (and thus shouldn't be prohibited like sentinel values should be), but in this particular case you're going out of your way to use it as a sentinel value. Please don't. Create a boolean that indicates whether or not your integer is valid, or create a new type that wraps the boolean and integer together.

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

Usually, when using newer versions of Java, I find I don't need to explicitly create or cast to the object versions of primitives because of the auto-boxing support that was added some time in 1.5 (maybe 1.5 itself).

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

#3


3  

I'd suggest using primitives all the time unless you really have the concept of "null".

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

Yes, the VM does autoboxing and all that now, but it can lead to some really wierd cases where you'll get a null pointer exception at a line of code that you really don't expect, and you have to start doing null checks on every mathematical operation. You also can start getting some non-obvious behaviors if you start mixing types and getting wierd autoboxing behaviors.

是的,VM会自动进行自动装箱以及所有这一切,但它可能导致一些非常奇怪的情况,你会在你真正不期望的一行代码中得到一个空指针异常,你必须开始进行空检查在每个数学运算上。如果您开始混合类型并获得奇怪的自动装箱行为,您也可以开始获得一些非显而易见的行为。

For float/doubles you can treat NaN as null, but remember that NaN != NaN so you still need special checks like !Float.isNaN(x).

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

It would be really nice if there were collections that supported the primitive types instead of having to waste the time/overhead of boxing.

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

#4


1  

In your example, the if statement will be ok until you go over 127 (as Integer autoboxing will cache values up to 127 and return the same instance for each number up to this value)

在您的示例中,if语句将一直存在,直到您超过127(因为Integer自动装箱将缓存最多127个值并为每个数字返回相同的实例,直到达到此值)

So it is worse than you present it...

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

if( i == 10 )

will work as before, but

会像以前一样工作,但是

if( i == 128 )

will fail. It is for reasons like this that I always explicitly create objects when I need them, and tend to stick to primitive variables if at all possible

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

#5


0  

Thee java POD types are there for a reason. Besides the overhead, you can't do normal operations with objects. An Integer is an object, which need to be allocated and garbage collected. An int isn't.

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

#6


0  

If that value can be empty, you may find that in your design you are in need of something else.

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

There are two possibilities--either the value is just data (the code won't act any differently if it's filled in or not), or it's actually indicating that you have two different types of object here (the code acts differently if there is a value than a null)

有两种可能性 - 要么值只是数据(如果填写或不填写,代码将不会有任何不同的行为),或者它实际上表明你在这里有两种不同类型的对象(如果存在,代码的行为会有所不同)值而不是null

If it's just data for display/storage, you might consider using a real DTO--one that doesn't have it as a first-class member at all. Those will generally have a way to check to see if a value has been set or not.

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

If you check for the null at some point, you may want to be using a subclass because when there is one difference, there are usually more. At least you want a better way to indicate your difference than "if primitiveIntValue == null", that doesn't really mean anything.

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

#7


0  

Don't switch to non-primitives just to get this facility. Use a boolean to indicate whether the value was set or not. If you don't like that solution and you know that your integers will be in some reasonable limit (or don't care about the occasional failure) use a specific value to indicate 'uninitialized', such as Integer.MIN_VALUE. But that's a much less safe solution than the boolean.

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

#8


0  

When you got to that 'Later on' point, a little more work needed to be accomplished during the refactoring. Use primitives when possible. (Capital period) Then make POJOs if more functionality is needed. The primitive wrapper classes, in my opinion, are best used for data that needs to travel across the wire, meaning networked apps. Allowing nulls as acceptable values causes headaches as a system 'grows'. To much code wasted, or missed, guarding what should be simple comparisons.

当你到达“后期”点时,在重构过程中需要完成更多的工作。尽可能使用基元。 (资本期)如果需要更多功能,则制作POJO。在我看来,原始包装类最适合需要通过网络传输的数据,这意味着网络应用程序。允许空值作为可接受的值会导致系统“增长”时出现问题。要浪费或错过很多代码,保护应该进行简单比较的内容。

#1


18  

Using the boxed types does have both performance and memory issues.

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

When doing comparisons (eg (i == 10) ), java has to unbox the type before doing the comparison. Even using i.equals(TEN) uses a method call, which is costlier and (IMO) uglier than the == syntax.

在进行比较时(例如(i == 10)),java必须在进行比较之前取消打包类型。即使使用statesquals(TEN)也会使用方法调用,这种方法调用比==语法更昂贵且(IMO)更加丑陋。

Re memory, the object has to be stored on the heap (which also takes a hit on performance) as well as storing the value itself.

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

A sneaky gotcha? i.equals(j) when i is null.

鬼鬼祟祟的骗局?当我为空时,等于(j)。

I always use the primitives, except when it may be null, but always check for null before comparison in those cases.

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

#2


8  

Firstly, switching from using a primitive to using an object just to get the ability to set it to null is probably a bad design decision. I often have arguments with my coworkers about whether or not null is a sentinel value, and my opinion is usually that it is not (and thus shouldn't be prohibited like sentinel values should be), but in this particular case you're going out of your way to use it as a sentinel value. Please don't. Create a boolean that indicates whether or not your integer is valid, or create a new type that wraps the boolean and integer together.

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

Usually, when using newer versions of Java, I find I don't need to explicitly create or cast to the object versions of primitives because of the auto-boxing support that was added some time in 1.5 (maybe 1.5 itself).

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

#3


3  

I'd suggest using primitives all the time unless you really have the concept of "null".

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

Yes, the VM does autoboxing and all that now, but it can lead to some really wierd cases where you'll get a null pointer exception at a line of code that you really don't expect, and you have to start doing null checks on every mathematical operation. You also can start getting some non-obvious behaviors if you start mixing types and getting wierd autoboxing behaviors.

是的,VM会自动进行自动装箱以及所有这一切,但它可能导致一些非常奇怪的情况,你会在你真正不期望的一行代码中得到一个空指针异常,你必须开始进行空检查在每个数学运算上。如果您开始混合类型并获得奇怪的自动装箱行为,您也可以开始获得一些非显而易见的行为。

For float/doubles you can treat NaN as null, but remember that NaN != NaN so you still need special checks like !Float.isNaN(x).

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

It would be really nice if there were collections that supported the primitive types instead of having to waste the time/overhead of boxing.

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

#4


1  

In your example, the if statement will be ok until you go over 127 (as Integer autoboxing will cache values up to 127 and return the same instance for each number up to this value)

在您的示例中,if语句将一直存在,直到您超过127(因为Integer自动装箱将缓存最多127个值并为每个数字返回相同的实例,直到达到此值)

So it is worse than you present it...

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

if( i == 10 )

will work as before, but

会像以前一样工作,但是

if( i == 128 )

will fail. It is for reasons like this that I always explicitly create objects when I need them, and tend to stick to primitive variables if at all possible

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

#5


0  

Thee java POD types are there for a reason. Besides the overhead, you can't do normal operations with objects. An Integer is an object, which need to be allocated and garbage collected. An int isn't.

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

#6


0  

If that value can be empty, you may find that in your design you are in need of something else.

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

There are two possibilities--either the value is just data (the code won't act any differently if it's filled in or not), or it's actually indicating that you have two different types of object here (the code acts differently if there is a value than a null)

有两种可能性 - 要么值只是数据(如果填写或不填写,代码将不会有任何不同的行为),或者它实际上表明你在这里有两种不同类型的对象(如果存在,代码的行为会有所不同)值而不是null

If it's just data for display/storage, you might consider using a real DTO--one that doesn't have it as a first-class member at all. Those will generally have a way to check to see if a value has been set or not.

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

If you check for the null at some point, you may want to be using a subclass because when there is one difference, there are usually more. At least you want a better way to indicate your difference than "if primitiveIntValue == null", that doesn't really mean anything.

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

#7


0  

Don't switch to non-primitives just to get this facility. Use a boolean to indicate whether the value was set or not. If you don't like that solution and you know that your integers will be in some reasonable limit (or don't care about the occasional failure) use a specific value to indicate 'uninitialized', such as Integer.MIN_VALUE. But that's a much less safe solution than the boolean.

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

#8


0  

When you got to that 'Later on' point, a little more work needed to be accomplished during the refactoring. Use primitives when possible. (Capital period) Then make POJOs if more functionality is needed. The primitive wrapper classes, in my opinion, are best used for data that needs to travel across the wire, meaning networked apps. Allowing nulls as acceptable values causes headaches as a system 'grows'. To much code wasted, or missed, guarding what should be simple comparisons.

当你到达“后期”点时,在重构过程中需要完成更多的工作。尽可能使用基元。 (资本期)如果需要更多功能,则制作POJO。在我看来,原始包装类最适合需要通过网络传输的数据,这意味着网络应用程序。允许空值作为可接受的值会导致系统“增长”时出现问题。要浪费或错过很多代码,保护应该进行简单比较的内容。