原始 "nulls" 和 Java 8
Primitive "nulls" and Java 8
我知道处理 null
基元的最佳做法是使用盒装包装器,例如 Integer
而不是 int
,如此处所述
Null for primitive data types
但是,今天 Java 8 仍然如此吗?
引入了可选原语,例如 OptionalInt
,其 OptionalInt.empty()
有效地表示 null
值?我的理解是 Optionals 应该只用于方法 return 类型,而不是属性本身的类型。 "nullable" 基元是否仍应存储为盒装属性?在方法 return 类型中,它们应该只是 Optional
吗?或者在 属性 本身中存储为 OptionalInt
?
假设你有一个方法
public void logRequest(Integer userID, String content){
//log content locally or in db or a rest call
}
假设您从数据库中获取特定用户的 userID
。可以有以下几种可能:
- 数据库returns一个用户ID。
- 未找到 ID。
所以现在如果没有找到 id,你将什么传递给 logRequest
?
- 你通过了
null
吗?如果该方法没有适当的空检查怎么办。它应该将 null
推断为什么?
- 你应该通过
0
还是 -1
?如果 null
表示 0
那么它可能与 ID 为 0
的实际用户相矛盾。怎么区分null和0?
当然,上述问题可以通过适当的文档和固定规则来解决,但它会增加维护成本并且容易出错。正确编写的代码应该是它自己的文档。
现在假设该方法声明为:
public void logRequest(OptionalInt userID, String content)
该方法本身非常清楚地表明它需要一个可选的参数。所以只有两种情况需要处理:发送一个适当的参数(这意味着使用 0
不绕过)。如果可选项为空,则方法正确地知道如何处理。 (不像以前我们必须阅读它的文档来预测行为)。
因此,它自己的方法声明清楚地说明了行为,而不是文档为您做这件事并依赖于文档和胡乱猜测。可选项在这种情况下非常有用。
PS:我总是觉得将 null
传递给期望 Integer
的方法很困难。绝对感觉不对!天哪,在 Java 中,潜意识总是将整数视为原始值。它们不应该被取消。
在您链接的问题的答案中,解释了为什么原始值不能是 null
。没有丝毫建议使用装箱值表示 null
是“最佳实践”,事实上,根本没有人说使用 null
是一个好的实践。
与其尝试为原始类型模拟 null
值,不如退后一步,想想您要表达的内容。可以使用多种选项表达该语义,使用 null
不一定是最佳选择之一。表达未初始化或“清除”值的一种简单方法是使用额外的 boolean fooPropertyInitialized
变量。它比使用 null
更清晰,而且它的开销不一定比原始值的装箱大。
这也适用于 API。不要使用 Optional
作为 属性。提供一个clearFooProperty()
方法即可,如果要支持这样的操作,再加一个isFooPropertyInitialized()
。如果调用者未能先检查 initialized
状态,让 getFooProperty()
抛出异常会更清晰。
请注意,也可以简单地使用特定上下文有效范围之外的值来表示特殊条件。这是常见的做法,例如 InputStream.read()
方法 returning -1
表示到达终点。
Optional
是 操作 的理想 return 类型,而结果没有存储空间,因此您不能拆分查询是否存在并且该值分为两次方法调用,现值的取值范围不限。
但是如前所述,在不知道您想表达哪种语义的情况下,我们无法提出建议,并且在没有“最佳实践”的情况下可能会有不止一种可能的解决方案……
我知道处理 null
基元的最佳做法是使用盒装包装器,例如 Integer
而不是 int
,如此处所述
Null for primitive data types
但是,今天 Java 8 仍然如此吗?
引入了可选原语,例如 OptionalInt
,其 OptionalInt.empty()
有效地表示 null
值?我的理解是 Optionals 应该只用于方法 return 类型,而不是属性本身的类型。 "nullable" 基元是否仍应存储为盒装属性?在方法 return 类型中,它们应该只是 Optional
吗?或者在 属性 本身中存储为 OptionalInt
?
假设你有一个方法
public void logRequest(Integer userID, String content){
//log content locally or in db or a rest call
}
假设您从数据库中获取特定用户的 userID
。可以有以下几种可能:
- 数据库returns一个用户ID。
- 未找到 ID。
所以现在如果没有找到 id,你将什么传递给 logRequest
?
- 你通过了
null
吗?如果该方法没有适当的空检查怎么办。它应该将null
推断为什么? - 你应该通过
0
还是-1
?如果null
表示0
那么它可能与 ID 为0
的实际用户相矛盾。怎么区分null和0?
当然,上述问题可以通过适当的文档和固定规则来解决,但它会增加维护成本并且容易出错。正确编写的代码应该是它自己的文档。
现在假设该方法声明为:
public void logRequest(OptionalInt userID, String content)
该方法本身非常清楚地表明它需要一个可选的参数。所以只有两种情况需要处理:发送一个适当的参数(这意味着使用 0
不绕过)。如果可选项为空,则方法正确地知道如何处理。 (不像以前我们必须阅读它的文档来预测行为)。
因此,它自己的方法声明清楚地说明了行为,而不是文档为您做这件事并依赖于文档和胡乱猜测。可选项在这种情况下非常有用。
PS:我总是觉得将 null
传递给期望 Integer
的方法很困难。绝对感觉不对!天哪,在 Java 中,潜意识总是将整数视为原始值。它们不应该被取消。
在您链接的问题的答案中,解释了为什么原始值不能是 null
。没有丝毫建议使用装箱值表示 null
是“最佳实践”,事实上,根本没有人说使用 null
是一个好的实践。
与其尝试为原始类型模拟 null
值,不如退后一步,想想您要表达的内容。可以使用多种选项表达该语义,使用 null
不一定是最佳选择之一。表达未初始化或“清除”值的一种简单方法是使用额外的 boolean fooPropertyInitialized
变量。它比使用 null
更清晰,而且它的开销不一定比原始值的装箱大。
这也适用于 API。不要使用 Optional
作为 属性。提供一个clearFooProperty()
方法即可,如果要支持这样的操作,再加一个isFooPropertyInitialized()
。如果调用者未能先检查 initialized
状态,让 getFooProperty()
抛出异常会更清晰。
请注意,也可以简单地使用特定上下文有效范围之外的值来表示特殊条件。这是常见的做法,例如 InputStream.read()
方法 returning -1
表示到达终点。
Optional
是 操作 的理想 return 类型,而结果没有存储空间,因此您不能拆分查询是否存在并且该值分为两次方法调用,现值的取值范围不限。
但是如前所述,在不知道您想表达哪种语义的情况下,我们无法提出建议,并且在没有“最佳实践”的情况下可能会有不止一种可能的解决方案……