【问题标题】:What's the best practice to avoid NPE against a collection field of a POJO/Java Bean?针对 POJO/Java Bean 的集合字段避免 NPE 的最佳实践是什么?
【发布时间】:2016-04-08 09:39:46
【问题描述】:

假设我有一个带有Collection 字段的 POJO/Java Bean 类:

public class Foo{//original code 
  private List<Bar> barList;

  public List<Bar> getBarList(){
    return this.barList;
  }

  public void setBarList(List<Bar> barList){
    this.barList=barList;
  }
}

getBarList() 被调用时如何避免 NPE?我的意思是getBarList() 可能会返回 null 并且调用者会抛出 NPE。假设我必须保持 setter 和 NULL 在这里没有语义。

public class Foo{//modified code. 
  private List<Bar> barList = new ArrayList<Bar>();//1) default value since construct to avoid this.barList being null.

  public List<Bar> getBarList(){
    return this.barList;
  }

  public void setBarList(List<Bar> barList){
    //2.a) check and throw a NPE
    Objects.requireNonNull(barList);
    //2.b) replace the NULL value
    if(barList==null){
        this.barList.clear();//less cost then `this.barList = new ArrayList<Bar>();`
    }else{
        this.barList=barList;
    }        
  }
}
  • 每个收集字段是否需要 1)?
  • 哪个更好,2.a) 还是 2.b)?为什么? Guava 选择了 2.a),例如 FluentIterable.from(null),但它不是 POJO/Java Bean。
  • POJO/Java Bean 是否有任何最佳实践或原则可以遵循以避免 NPE?我认为修改后的代码不再是 setter 中某些逻辑代码的 POJO。

谢谢!

【问题讨论】:

  • 2 和 3 解决不了任何问题,因为您不能确定在调用 getter 之前调用了 setter
  • @Anderson 我删除了我的评论,但你的说法是错误的。 this.barListbarList 是不同的变量,并导致 setter 方法的行为不同。
  • getBarList() 可能会返回 null,但绝不会抛出 NPE!
  • @Anderson 删除断言,默认情况下它们在 Java 中未启用。如果您真的想避免所有麻烦:在 foo 实例化时实例化 List,并且不允许 setter 设置 null 值。
  • 我不会将以上所有内容视为一种模式。有许多因素使该设计合理或无用。没有一种单一的“万能”解决方案可以避免客户端出现 NPE。大多数情况下,您没有幸运地从头开始创建设计,并且可以首先应用模式。大多数情况下,你必须“用你得到的东西工作”。其中可以包括“不能返回不可变列表”或“客户端依赖 NULL 来获取语义”...

标签: java nullpointerexception javabeans pojo


【解决方案1】:

初始化声明中的字段。请注意,您的代码无法编译 - 您无法实例化 List,请尝试:

private List<Bar> barList = new ArrayList<Bar>();

原因是:

  • 实例化列表非常便宜
  • 这是完成工作的最少代码
  • 延迟初始化的最大问题是线程安全 - 它需要特殊代码

【讨论】:

  • 如果setBarList(null) 会被调用怎么办?恕我直言,他也必须检查一下。
  • @Fil 最好删除setter。这没有任何意义,因为其他客户会期望他们添加的内容仍然存在
  • 我同意。不过,您应该将此添加到您的答案中。我没有认为这是理所当然的。
  • 感谢您指出我的错误ArrayList,我已经更新了。假设我必须保留二传手。更何况,你的意思是删除集合字段的setter是最佳实践吗?
【解决方案2】:

这个问题不是发生在setter,而是getter。

1) 可能会导致大量开销,因为某些List 实现具有支持数据容器的初始大小(例如ArrayList)。

2) 没有意义,因为您仍然会遇到异常。解决不了问题。

3) 是正确的方法,但不是在 setter 中!将它放在 getter 中:

if(this.barList==null){
  this.barList = new LinkedList<Bar>();
}

【讨论】:

  • 我宁愿使用java.util.Collections.emptyList() 而不是创建一个新列表。
  • 问题不在于getter,也不在于getter的结果可能被使用的地方。谁说'null'无效?
  • @hinneLinks 为什么?这会限制您操纵列表。
  • 你不能调用new List&lt;Bar&gt;() ...另外,懒惰的创建可能会带来并发问题。
  • @Fildor 你是对的,当然你需要一个List 的实现。是的,这些问题是有效的。虽然不在我的回答范围内:D
【解决方案3】:

使用

private List<Bar> barList = new List<Bar>();

一般来说,当您有一个返回 List 的 getter 方法时,最好返回空列表而不是 null 值。

【讨论】:

  • 这不会编译。我同意 Stultuske 的观点,他会主张在这里使用“更好”这个词。 Null 可能具有语义含义。因此,返回空集合而不是 null 并不总是“更好”。尤其是在更改遗留代码时,您可能会以这种方式破坏应用程序逻辑。
  • @Fildor 是的,我正在处理一些遗留代码。而且我确信在这种情况下 NULL 没有语义意义。我应该告诉更多关于它的上下文,再次抱歉。
【解决方案4】:

您可以使用特殊情况 null 并返回一个空列表。

public List<Bar> getBarList() {
    return barList != null ? barList : Collections.<Bar>emptyList();
}

或者你可以从空列表开始

  private List<Bar> barList = Collections.<Bar>emptyList();

【讨论】:

  • 那个emptyList不是不可变的吗?
  • @Fildor - 是的 - 这会是个问题吗?
  • @OldCurmudgeon 抱歉,不能接受不可变列表,因为调用者可能会执行getBarList().add(aBar); 出于同样的原因,getter 中的可变空列表也不可能。
  • 或者你的意思是 POJO 必须是不可变的?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-11-12
  • 2016-12-22
  • 1970-01-01
  • 2010-11-28
  • 2011-10-19
  • 2020-11-03
  • 2016-11-25
相关资源
最近更新 更多