可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Reference: http://java.sun.com/j2se/1.5.0/docs/guide/language/autoboxing.html
"If your program tries to autounbox null, it will throw a NullPointerException."
javac will give you a compile-time error if you try to assign null to a boolean. makes sense. assigning null to a Boolean is a-ok though. also makes sense, i guess.
but let's think about the fact that you'll get a NPE when trying to autounbox null. what this means is that you can't safely perform boolean operations on Booleans without null-checking or exception handling. same goes for doing math operations on an Integer.
for a long time, i was a fan of autoboxing in java1.5+ because I thought it got java closer to be truly object-oriented. but, after running into this problem last night, i gotta say that i think this sucks. the compiler giving me an error when I'm trying to do stuff with an uninitialized primitive is a good thing. I dont want to use autoboxing if I lose that.
I think I may be misunderstanding the point of autoboxing, but at the same time I will never accept that a boolean should be able to have 3 values. can anyone explain this? what am i not getting?
回答1:
Boxed types are reference types, and all reference types, primitive boxes or not, can refer to null
. That's why a Boolean
can refer to null
. So can an Integer
. So can a String
, etc.
Boxed types are not designed to make Java truly object oriented. Java will never be a purely object oriented language, and you should not code as if this is the case. Primitive types will never go away, and in fact should be preferred whenever there's a choice.
Here's a quote from Effective Java 2nd Edition, Item 49: Prefer primitive types to boxed primitives (emphasis by author):
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.
回答2:
I've seen at least one case where the null
value is useful. Lots of data objects in webservices have nullable boolean fields. Sometimes the user neglects to include a value. In this case you want to be able to discern the lack of a value from a default value. Previously, people would write getX
, setX
, and isXSet()
methods, where isXSet
returns false until someone calls setX
. Now it's possible to make X
be a nullable type and it's clear that it wasn't set if getX
returns null
.
回答3:
In addition to everything said here, there are cases where you would very much want to have a third value for booleans - the case of an "optional" property.
We usually encounter it with databases, with boolean columns that allow nulls. Without using Booleans, we would need to use two separate variables, one indicating the value and another whether it is valid.
In fact, if you look at the JDBC API, you can see an example of this problem, where columns get a default value if they are null (e.g., a 0 for numeric fields), and then you have to call "wasNull" to check whether it is a true 0 or a fake null!
回答4:
Your issue is with autounboxing, not autoboxing. I think autounboxing is evil for more significant reasons:
Consider this:
Integer i = new Integer(1);
Integer i2 = new Integer(12);
System.out.println(i == 10 || i != i2);
One ==
unboxes and the other doesn't.
Unboxing on operators (as opposed to assignments) was a mistake in my view (given the above - it is just not Java). Boxing, however, is very nice.
回答5:
I think it's more philosophical than technical question. When you transform primitive type to reference type you should be ready that reference types (i.e. objects) are nullable.
You can watch The Billion Dollars Mistake presentation where C.A.R. Hoare says that his introducing null references to oop (Algol 60) was a mistake.
Josh Bloch in Effective Java recommends to prefer primitive types where it's possible. But sometimes you do have to verify you Boolean variable against null.
回答6:
There is actually no big difference to the days before Java 1.5 - the problem is not the boolean type (it still has two states) but the Boolean wrapper (which always had 3 states. Boolean.TRUE, Boolean.FALSE and null).
Every conversion from a Boolean object to the boolean primitive requires null checks, with or without autoboxing.
回答7:
Autoboxing automatically transforms data types between intrinsic types and Object types.
Object types for Boolean can be Boolean.TRUE
, Boolean.FALSE
, or null
. That's why you have to deal with the possible 3 values for a boolean.
In databases, it is common to have three states for a boolean type. Consider a record that tracks whether someone has passed a class. There's TRUE, for passing; FALSE for not passing, and NULL for "currently taking the class". Yes, it's odd, but not having a value is inherit in object oriented programming.
I too find autoboxing a bit distasteful, mainly because it is a feature where the compiler adds bytecode to handle the conversion process. In my opinion, this can lead to people forgetting about important details in the conversion process which might be better remembered (such as the null handling in your case). It is useful, but not nearly as useful as most other features of the Java language.
Personally, I wish that the intrinsics were implemented as lightweight objects instead of "built-in" types. There's a lot of times where the hybrid intrinsic / object type system gets in the way. That said, intrinsics were supposed to be there to improve performance, but it seems that if you must do a lot of intrinsic to Object marshalling, you can't enjoy the intrinsic-only performance boost.
回答8:
It is the problem with autoboxing, just like Integer i = null;
. Integer
object can be null while a native int
cannot be.
回答9:
It was never intended, with the introduction of autoboxing, that it replace the primitives. In most places primitives are what you want. If you want to start using the reference types because "it got java closer to be truly object-oriented" then that's your call, but as you are discovering, there are disadvantages with that approach. Performance will be another issue.
Autoboxing (and autounboxing) is there to help with the transitions between code that uses primitives (the majority) and the code that has to use reference types (rare). Of the reference types, Boolean is undoubtedly the rarest, since its small number of instances mean it's almost never worth putting in a Collection.
In summary: if the reference types are causing you trouble, don't use them. But don't say 'autoboxing is bad" just because they turned out not to be helpful in your situation.