我觉得这个问题的前提已经不正确了。您不必在大多数地方检查空字符串——事实上,我认为您应该尽可能避免使用null,但尤其是,当存在另一个非空标记值时。而String已经有了一个很好的“空”值:空字符串("")!
如果"" 和" " 需要折叠成相同的值,那么标准库中已经有一个非常好的方法:.trim()。但是,.trim() 作为String 上的实例方法,仅适用于非空字符串。这不一定是坏事!
如果null 和"" 对您来说意味着不同的东西,那么我认为您的数据模型太复杂了,您应该使用其他包装类而不是使用@987654332 @ 直接地。如果null 和"" 表示相同的意思,那么您应该选择其中一个,并始终如一地使用它。这可能意味着需要进行一些!= null 检查,但如果您发现自己在整个代码库中经常 需要isNullOrEmpty 或isNotBlank 辅助函数,我会说这是代码异味,并且您真的应该致力于解决底层数据模型问题,而不是担心一个微小的辅助函数。
这是什么意思?在Avoiding != null statements 问题中,投票最多的答案指出,实际上只有两种类型的值可以为 null:(1) null 是有效值,或者 (2) null 不是有效值价值。
案例 (2) 不是很有趣。 Null 不是一个有效值,所以我们不应该尝试处理它。如果有的话,我们只要遇到它就抛出一个异常。否则我们忽略它,让NullPointerException“自然”发生。它不应该为 null,因此根据定义,找到 null 是一种例外情况。
如果 null 是一个有效值,那么它意味着 null 具有语义。这很可能意味着一个值“不存在”或“无效”。这里有两种子情况:(1a) null 表示与空字符串相同的意思,或者 (1b) 表示不同的意思。
如果您有案例 (1b),那么我认为您需要在域模型中添加一个新实体。例如,您可以创建一个像PaymentTerm 这样的类,它具有单独的.isValid() 和.isPresent() 方法,以及一个.asString() 访问器来获取字符串值(如果存在)。 (创建PaymentTerm 类有很多可能的方法,有很多可能的权衡:重点不是您需要这种特定的形式,而是您需要一些东西 而不是原始的@ 987654345@,你可以挂起方法,因为这个东西现在是你领域模型中的一流实体。)
如果你有 case (1a),那么 null 和空字符串在语义上都是同一个意思。但是它们在语法上是非常不同的!空字符串已经有一个实例方法来检查它(.isEmpty()),并且可以安全地存储、传递、与其他字符串相比等。
所以情况 (1a) 有两种可能的解决方案:(1a.1) 你传递 null 和空字符串,并且在任何地方你都必须检查,或者 (1a.2) 你将 null 标准化为空字符串最快的机会,然后将 null 视为无效值,并在任何地方使用空字符串。根据您的输入格式,您甚至可以“免费”获得此行为(例如,空文本框自然会将空字符串作为值,而不是 null)。
我的论点是案例 (1a.1) 是代码异味。而不是同时传递 null 和空字符串,并经常检查两者(手动或使用 isNullOrEmpty 或 isNotBlank 之类的方法),您应该尝试进入 case (2) 或 case (1a.2) .
请注意,这个答案实际上意味着 isNotBlank 和 != null 都不是最佳的!在一个结构良好的代码库中,您应该努力避免这两种情况,但我倾向于认为您应该更努力地避免像 isNotBlank 这样的东西。
也就是说,如何检查 null 或空字符串并不是非常重要。无论如何,JIT 几乎肯定会内联检查,并且在许多情况下,如果它可以用另一种方式证明零安全性,窥视孔优化器将完全忽略它们。要考虑的更重要的事情是null 在你的程序中是否是一个有效的值,如果是,它在语义上的含义意味着一个值是否为空。