在Java中优雅地避免NullPointerException

25

考虑以下代码:

if (object.getAttribute("someAttr").equals("true")) { // ....

显然这行代码有潜在的bug,属性可能为null,我们会得到一个NullPointerException。所以我们需要将其重构为以下两种选择之一:

第一个选择:

if ("true".equals(object.getAttribute("someAttr"))) { // ....

第二个选项:

String attr = object.getAttribute("someAttr");
if (attr != null) {
    if (attr.equals("true")) { // ....

第一种选项阅读起来比较困难,但更加简洁,而第二种选项意图清晰,但冗长。

就可读性而言,您更喜欢哪个选项?

9个回答

27

我一直使用的是

if ("true".equals(object.getAttribute("someAttr"))) { // ....

因为尽管它阅读起来有点困难,但更加简洁,我认为它足够易读,所以您很容易习惯它


18

在第二种选择中,你可以利用短路运算符&&:

String attr = object.getAttribute("someAttr");
if (attr != null && attr.equals("true")) { // ....

1
确实可以这样做,但这是以优化为重而牺牲可读性,这总是一个不好的想法。 - David Pierre
7
哦,偏爱优化?我认为相反,这比选项1稍微更易读。 - Jonik
1
是的 - 样式问题没有明确的答案。这是你所使用的。就个人而言,我已经习惯了这种风格,因为它也适用于许多其他从C/C++语法派生的语言。如果您的组织/项目有代码约定,请遵循其建议。 - laalto
7
这并不是“优化”,也不会导致可读性降低。我认为使用两个if语句实际上可读性更差,因为你会得到比实际需要的更多嵌套块。同时,这种双重if语句还会引发臭名昭著的“2ifs1else”漏洞:“如果(a)如果(b)则打印(“a和b”);否则打印(“不是a和b”);”。 - gustafc

2

有些情况下,简洁的方法一开始可能感觉不对,但实际上会变得习惯。这是其中之一;另一个情况是:

String line;
while ((line = bufferedReader.readLine()) != null) {
  // Use line
}

在一个条件语句中出现副作用?这是不可想象的!但是,当你认识到特定的模式时,它实际上比其他选择更好。

这种模式很常见,尤其在Java中,我认为任何有经验的开发人员都应该能够识别它。结果非常简洁。(有趣的是,我有时会看到使用相同习惯用法的C#代码,但在C#中,等号运算符与字符串一起使用也很好用。)

最重要的是:使用第一个版本,并熟悉它。


1

我喜欢选项1,而且我认为它足够易读。

顺便说一下,选项3是引入一个带有默认值参数的getAttribute方法。


这个问题与“if”块有关,所以关于您的选项3:“您不能总是指望该方法不会返回空值。” - victor hugo
当然,你可以确保它永远不会返回空值作为默认值。 - willcodejavaforfood
选项3将完全根据DRY原则将错误处理和空值检查移入方法中。 - willcodejavaforfood
我同意,但是我的意思是你并不总是测试你编写的方法,所以你不能依赖返回值不会为空。 - victor hugo

1

在功能等效的情况下,始终追求更短的代码。特别是在像这样不会牺牲可读性的情况下。


0

我有另一个答案;

List<Map<String, Object>> group = jjDatabase.separateRow(db.Select("SELECT * FROM access_user_group  WHERE user_id=1 ;"));

在我的数据库中'access_user_group'表中没有"group_c80"这一列,所以在get(0).get("group_c80")时会发生空指针异常。但我通过以下代码进行了处理:
for (int j = 1; j < 100; j++) {
                    String rulId="0";//defult value,to privent null pointer exeption in group_c
                    try {
                        rulId = group.get(0).get("group_c" + j)).toString();
                    } catch (Exception ex) {
                        ServerLog.Print( "Handeled error in database for " + "group_c" + (j < 10 ? "0" + j : j) +"This error handeled and mot efect in program");
                        rulId = "0";
                    }}

0

这是我的方法,需要一个PropertyUtil类,但只需编写一次:

/**
 * Generic method to encapsulate type casting and preventing nullPointers.
 * 
 * @param <T>          The Type expected from the result value.
 * @param o            The object to cast.
 * @param typedDefault The default value, should be of Type T.
 * 
 * @return Type casted o, of default.
 */
public static <T> T getOrDefault (Object o, T typedDefault) {
    if (null == o) {
        return typedDefault;
    }
    return (T) o;
}

客户端代码可以这样做:

PropertyUtil.getOrDefault(obj.getAttribute("someAttr"), "").equals("true");

或者,对于一个列表:

PropertyUtil.getOrDefault(
    genericObjectMap.get(MY_LIST_KEY), Collections.EMPTY_LIST
).contains(element);

或者对于 List 的使用者,它会拒绝 Object:

consumeOnlyList(
    PropertyUtil.getOrDefault(
        enericObjectMap.get(MY_LIST_KEY), Collections.EMPTY_LIST
    )
)

默认情况下可能是空对象模式的实现https://en.wikipedia.org/wiki/Null_Object_pattern


0

Util.isEmpty(string) - 返回 string == null || string.trim().isEmpty() Util.notNull(string) 如果 string == null,则返回 "",否则返回 string。 Util.isNotEmpty(string) 返回 ! Util.isEmpty(string)

我们有一个约定,对于字符串而言,Util.isEmpty(string) 语义上表示为 true,Util.isNotEmpty(string) 语义上表示为 false。


0
这是一个非常好的问题。 我通常使用不太优雅的方式:
if (object.getAttribute("someAttr") != null && object.getAttribute("someAttr").equals("true")) { // ....

(我将不再使用它)


4
好的!如果 getAttribute() 实际上具有副作用怎么办?有时您会运行一次,有时您会运行两次。 - Vincent Robert
我可能会在这里看到的“常见编程错误”问题中写下这个。 - victor hugo
感谢您的评论。我知道这不好,我来这里是为了学习这样的东西。还有一个问题:如果getAttribute()只是一个标准的getter方法 - 因此没有副作用,那么这种方法有什么问题? - alexmeia
1
+1 我认为有些人只看代码,不看文本。在评论中的@question:它可能会在以后产生副作用。在getter中不太可能造成严重的损害,但是有可能。例如,如果它在下一个版本中返回计算数据,可能会导致性能损失 - 特别是如果此代码到处都是。 - soulmerge
谢谢soulmerge,我明白了问题。我认为像这样的东西stackoverflow很不错。 - alexmeia
加1也一样。但这不是问题的答案。 - juanmf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接