什么时候应该使用布尔值的空值?

Posted

技术标签:

【中文标题】什么时候应该使用布尔值的空值?【英文标题】:When should null values of Boolean be used? 【发布时间】:2012-06-26 11:19:05 【问题描述】:

Java boolean 允许 truefalse 的值,而布尔值允许 truefalsenull。我已经开始将我的booleans 转换为Booleans。这可能会导致测试崩溃,例如

Boolean set = null;
...
if (set) ...

测试时

if (set != null && set) ...

似乎做作并且容易出错。

如果有的话,什么时候使用带有空值的Booleans 有用吗?如果从来没有,那么包裹对象的主要优点是什么?

更新: 有很多有价值的答案,我在自己的答案中总结了一些。我充其量只是 Java 的中级,所以我试图展示我认为有用的东西。请注意,问题是“措辞不正确”(布尔值不能“具有空值”),但我已将其保留以防其他人有同样的误解

【问题讨论】:

有时,您想要一个未初始化的状态,将Boolean 变量设置为null 会有所帮助。 “Always”有点强,我不敢确认,但如果它真的被用作第三种状态,我希望对 null 进行测试。 你有理由将布尔值转换为布尔值吗?我会坚持使用原始类型并仅在有充分理由这样做的情况下包装它,例如当我需要通过引用传递变量时。 您可能还想看看这个:thedailywtf.com/Articles/What_Is_Truth_0x3f_.aspx 没有“空值 in 布尔值”这样的东西。 Boolean 是一个对象,而 boolean 是一个“标量”。如果 Boolean 引用设置为 null,则表示相应的 Boolean 对象不存在。你不能把任何东西放在不存在的东西里面。 【参考方案1】:

当您需要三种状态时,布尔值非常有用。就像在软件测试中一样,如果测试通过发送 true,如果失败发送 false,如果测试用例中断发送 null,这将表示测试用例未执行。

【讨论】:

枚举不是最适合这个吗?与可能为客户端产生意外的 NullPointerExceptions 相反,枚举将明确定义“状态” 在状态机中总是更喜欢枚举而不是布尔值。您永远不知道新的第四个状态何时到达业务需求。【参考方案2】:

对于以上所有好的答案,我将在 Java servlet HttpSession 类中给出一个具体示例。希望这个例子有助于澄清您可能仍然有的一些问题。

如果您需要存储和检索会话的值,请使用 setAttribute(String, Object) 和 getAttribute(String, Object) 方法。因此,对于布尔值,如果要将其存储在 http 会话中,则必须使用 Boolean 类。

HttpSession sess = request.getSession(false);
Boolean isAdmin = (Boolean) sess.getAttribute("admin");
if (! isAdmin) ...

如果未设置属性值,最后一行将导致NullPointerException。 (这就是导致我写这篇文章的原因)。因此,无论您是否愿意使用,这 3 个逻辑状态都将保留。

【讨论】:

【参考方案3】:

尽可能使用boolean 而不是Boolean。这将避免许多NullPointerExceptions 并使您的代码更加健壮。

Boolean很有用,例如

将布尔值存储在集合(列表、地图等)中 表示一个可为空的布尔值(例如,来自数据库中的一个可为空的布尔值列)。在这种情况下,null 值可能意味着“我们不知道它是真还是假”。 每次方法需要一个对象作为参数时,您都需要传递一个布尔值。例如,当使用反射或 MessageFormat.format() 等方法时。

【讨论】:

数据库中的空值也可能意味着"FileNotFound" 我认为,您的第二个项目符号确实是这个问题正确答案的核心。 布尔值的另一个用途是在扩展泛型类时作为泛型类型参数 - 与第 3 点密切相关。 用“宇宙不知道”以外的含义重载 null 的概念,它比使用 3(或更多)值的枚举更弱。也使将参数传递到方法中的代码读取更加明确。 或#4:Boolean isSchrodinger‎CatAlive = null;(抱歉,无法抗拒;))。【参考方案4】:

在布尔元素的严格定义中,只有两个值。在一个完美的世界里,这是真的。在现实世界中,元素可能丢失或未知。通常,这涉及用户输入。在基于屏幕的系统中,它可以通过编辑来强制执行。在使用数据库或 XML 输入的批处理世界中,该元素很容易丢失。

因此,在我们生活的不完美世界中,布尔对象非常棒,因为它可以将缺失或未知状态表示为空。毕竟,计算机只是对现实世界进行建模,应该考虑所有可能的状态并通过抛出异常来处理它们(主要是因为在某些用例中抛出异常是正确的响应)。

在我的例子中,布尔对象是完美的答案,因为输入 XML 有时会丢失元素,我仍然可以获取一个值,将其分配给布尔值,然后在尝试使用真或假之前检查是否为空用它测试一下。

只要我的 2 美分。

【讨论】:

【参考方案5】:

我几乎从不使用Boolean,因为它的语义含糊不清。基本上你有三态逻辑:真、假或未知。有时使用它很有用,例如您让用户在两个值之间进行选择,而用户根本没有回答,您真的想知道该信息(想想:NULLable 数据库列)。

我认为没有理由从boolean 转换为Boolean,因为它会带来额外的内存开销、NPE 可能性和更少的输入。通常我会使用尴尬的BooleanUtils.isTrue() 来让Boolean 让我的生活更轻松。

Boolean 存在的唯一原因是能够拥有Boolean 类型的集合(泛型不允许boolean 以及所有其他原语)。

【讨论】:

不过,它是一个外部库(Apache commons)。 对于多值逻辑,最好使用枚举。因此,布尔值应该留给(自动)装箱变量,以便在数据结构中使用。 使用布尔值时,避免空指针异常的方便测试是Boolean.TRUE.equals(myBooleanObject)Boolean.FALSE.equals(myBooleanObject) 布尔对象只有两种状态——truefalsenull 不是对象的状态,而是对象引用的状态。 让 apache commons 像“有人可能会搞砸评估布尔值......让我们创建一个 isTrue() 方法......”这听起来像是实用函数历史上最愚蠢的事情。然而,不知何故,它很有用......这是这里最大的wtf。【参考方案6】:

回答自己的问题: 我认为回答我自己的问题会很有用,因为我从答案中学到了很多东西。这个答案旨在帮助像我这样对这些问题没有完全理解的人。如果我使用了不正确的语言,请纠正我。

空“值”不是一个值,与truefalse 有根本的不同。这是没有指向对象的指针。因此认为布尔值是 3 值是根本错误的

布尔的语法被缩写并隐藏了引用指向对象的事实:

Boolean a = true;

隐藏了true 是一个对象的事实。其他等效的分配可能是:

Boolean a = Boolean.TRUE;

Boolean a = new Boolean(true);

缩写语法

if (a) ...

与大多数其他赋值不同,它隐藏了 a 可能是对象引用或原语的事实。如果一个对象有必要测试null 以避免NPE。对我来说,如果有平等测试,在心理上更容易记住这一点:

if (a == true) ...

我们可能会被提示测试是否为空。因此,只有当a 是原语时,缩短的形式才是安全的。

对于我自己,我现在有以下建议:

切勿将 null 用于 3 值逻辑。只使用真假。 永远不要从方法中返回Boolean,因为它可能是null。只返回boolean。 仅使用 Boolean 将元素包装在容器中,或用于需要对象的方法的参数

【讨论】:

不要使用“new Boolean(whatever)”。这将在堆上实例化一个新的布尔值。然而布尔值是不可变的。使用“Boolean.valueOf(whatever)”,它将创建一个指向 Boolean.TRUE 或 Boolean.False 的引用,具体取决于任何内容。 Java 的许多问题之一(恕我直言,12 年后)是旧语言也具有的 null 值方法不一致。看看 Scala,它有一个类型化 Option 的概念,它可以为 null 或可以有一个值(子类 None 或 Some)。然后你可以调用 myOption.getOrElse(defaultValue)。请参阅scala-lang.org/api/current/scala/Option.html,此功能并不复杂。然而,由于它内置在新的 JVM 语言中,因此许多库都使用它。这使得 scale “修复”了 Java 的一些“上世纪”问题,但它仍然编译为在 JRE 上运行的类文件。【参考方案7】:

布尔包装器中的**null** 值有很多用途! :)

例如,您可能在表单中有一个名为“newsletter”的字段,用于指示用户是否想要来自您网站的时事通讯。如果用户未在此字段中选择值,您可能希望针对该情况实施默认行为(发送?不发送?,再次提问?等)。显然,未设置(或未选中或**null**),不等于true或false。

但是,如果“未设置”不适用于您的模型,请不要更改布尔原语;)

【讨论】:

【参考方案8】:

最好的方法是完全避免使用布尔值,因为每个布尔值都意味着您在代码中的其他任何地方都有一个条件语句(请参阅http://www.antiifcampaign.com/ 和这个问题:Can you write any algorithm without an if statement?)。

但是,实际上,您必须不时使用布尔值,但是,正如您自己已经发现的那样,处理布尔值更容易出错,也更麻烦。所以我建议尽可能使用布尔值。例外情况可能是具有可为空布尔列的旧数据库,尽管我也会尝试将其隐藏在我的映射中。

【讨论】:

处理布尔值与处理任何其他对象一样容易出错。顺便说一句,该链接反对用于类型检查的 IF 的扩散,而不是用于一般用途。反过来,这也是“危险的”,因为它使修改更加困难。所以 IF 本身并没有什么问题。 不是问题的答案。 @MisterSmith Imho 布尔标志经常被误用作类型检查变量,因此链接也可以在这里应用。这应该只是暗示人们应该考虑在某种情况下布尔值是否是正确的工具。 “完全避免布尔值”这句话当然非常激进,实际上是不可能的,这就是我添加第二段的原因。我还添加了一个“更容易出错和更麻烦”来澄清事情。【参考方案9】:

原语的包装类可用于需要对象的地方,集合是一个很好的示例。

假设您出于某种原因需要将boolean 的序列存储在ArrayList 中,这可以通过将boolean 装箱到Boolean 中来完成。

关于这个here有几句话

来自文档:

正如任何 Java 程序员都知道的那样,您不能将 int(或其他原语 值)到一个集合中。集合只能保存对象引用, 所以你必须将原始值装箱到适当的包装类中 (在 int 的情况下是整数)。当你把物体从 集合,你得到你放入的整数;如果你需要一个 int,您必须使用 intValue 方法将 Integer 拆箱。所有这些 装箱和拆箱是一件很痛苦的事情,并且会弄乱你的代码。这 自动装箱和拆箱功能使过程自动化,消除了 痛苦和混乱。

http://docs.oracle.com/javase/1.5.0/docs/guide/language/autoboxing.html

【讨论】:

【参考方案10】:

哇,到底是什么?是我一个人,还是所有这些答案都是错误的,或者至少是误导性的?

布尔类是布尔基本类型的包装器。这个包装器的用途是能够在接受对象或泛型的方法中传递布尔值。即向量。

布尔对象永远不能有 null 值。如果您对 Boolean 的 reference 为 null,则仅表示您的 Boolean 从未创建过。

您可能会发现这很有用:http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/Boolean.java

一个空布尔引用应该只用于触发您有任何其他空引用的类似逻辑。将其用于三态逻辑很笨拙。

编辑:请注意,Boolean a = true; 是一种误导性陈述。这真的等于更接近Boolean a = new Boolean(true); 请在此处查看自动装箱:http://en.wikipedia.org/wiki/Boxing_%28computer_science%29#Autoboxing

也许这就是很多混乱的来源。

EDIT2:请阅读下面的 cmets。 如果有人知道如何重组我的答案以纳入这一点,请这样做。

【讨论】:

我不明白你的说法“布尔值永远不能有空值”。我可以创建一个布尔值 (Boolean a = true;),然后将 a 设置为 null (a = null;)。它可能不优雅或不明智,但它是可能的。 当你做Boolean a; a 是指向布尔对象的指针。如果您 a = null; 您尚未将布尔值设置为 null,则您已将引用设置为 null。执行Boolean a = null; a.booleanValue(); 在这种情况下,您甚至从未创建 布尔对象,因此它会抛出空指针异常。如果您需要进一步的指导,请告诉我。 此外,当您执行 Boolean a = true; 时,它会产生某种魔法,实际上会被解释为 Boolean a = new Boolean(true); 出于性能原因,这可能并不完全正确,但您必须意识到布尔值仍然是一个对象。 @missingno,所有处理任何对象的代码都必须处理这个。对象引用可以为空,也可以不为空。这不是特殊情况,不需要特别考虑。 你错过了我的观点@missingno。我同意我们需要考虑空参考值。我从来没有反对那个。但我们需要为 ANY OBJECT REFERENCE 执行此操作。空布尔引用不是特殊情况。因此 null 布尔参考值不需要特别考虑。【参考方案11】:

我想在某些情况下,您应该有一种机制来区分已经设置值的布尔字段。

【讨论】:

【参考方案12】:

Boolean 包装器在您想要除 truefalse 之外是否已分配值时很有用。它有以下三种状态:

是的 错误 未定义null

boolean 只有两种状态:

是的 错误

上述差异将有助于在 Boolean 值列表中,它可以有 TrueFalseNull

【讨论】:

不,它没有 null 状态,这就是引用。 我的意思是布尔可以用于已定义的真/假和未定义。【参考方案13】:

布尔值的主要用途是空值。 空值表示,该属性未定义,例如取数据库可为空的列。

如果您确实需要将原始布尔值转换为包装布尔值,那么您可以使用以下代码来支持旧代码:

Boolean set = Boolean.FALSE; //set to default value primitive value (false)
...
if (set) ...

【讨论】:

'主要目的是空值'。不,Boolean 的主要目的是将对布尔值的引用作为对象传递。 @user606723 同意,我在写的时候指的是数据库案例。【参考方案14】:

有三个快速的原因:

表示数据库布尔值,可以是truefalsenull 表示用xsd:nillable="true" 声明的XML Schema 的xsd:boolean 值 能够使用泛型类型:List<Boolean> - 你不能使用List<boolean>

【讨论】:

我明确写了“布尔”,而不是“boolean”(思维定式),因为在 Oracle 中,您通常使用带有“T”和“F”值的 char(1) null。所以它可能(例如,Hibernate 类型适配器)为 null :) 什么数据库不允许布尔值为空?如果将列设置为可为空,则数据类型不再重要... @MichalB。 Sybase(和 SQL Server)位类型infocenter.sybase.com/help/index.jsp?topic=/… @Mark - 不,SQL Server 确实允许可空位 - msdn.microsoft.com/en-us/library/ms177603.aspx

以上是关于什么时候应该使用布尔值的空值?的主要内容,如果未能解决你的问题,请参考以下文章

用于空值的空值检查运算符

mysql中的空值问题

我应该为 iOS 布尔状态使用啥值?

python1

python---布尔值和空值

RODBC:为啥 sqlQuery() 中的空值和仅空格值的值为“NA”?