为什么返回空集合被视为好习惯?

时间:2016-04-21 20:11:12

标签: java collections nullpointerexception null immutability

我已阅读了几本书并看过几篇博客,讨论如何返回空集合比返回null更好。我完全理解试图避免检查,但我不明白为什么返回空集合比返回null更好。例如:

public class Dog{

   private List<String> bone;

   public List<String> get(){
       return bone;
   }

}

VS

 public class Dog{

   private List<String> bone;

   public List<String> get(){
       if(bone == null){
        return Collections.emptyList();
       }
       return bone;
   }

}

示例一将抛出NullPointerException,示例二将抛出UnsupportedOperation异常,但它们都是非常通用的异常。是什么让一个人比另一个好或坏?

另外一个选择是做这样的事情:

 public class Dog{

   private List<String> bone;

   public List<String> get(){
       if(bone == null){
        return new ArrayList<String>();
       }
       return bone;
   }

}

但问题在于您是否在其他人可能必须维护的代码中添加了意外行为。

我真的在寻找解决这种困境的方法。博客上的许多人倾向于只是说没有详细解释为什么会更好。如果返回一个不可变列表是最好的做法,我可以这样做,但我想了解为什么它更好。

5 个答案:

答案 0 :(得分:7)

如果您返回一个空集合(但不是必然 Collections.emptyList()),您可以避免使用无意的NPE对此方法的下游消费者感到惊讶。

这比返回null更可取,因为:

  • 消费者无需防范
  • 消费者可以对集合进行操作,而不管其中包含多少元素

我说不一定是Collections.emptyList(),因为正如你所指出的那样,你正在为另一个运营商交换一个运行时例外,因为添加到此列表将不受支持,并再次让消费者感到惊讶。

最理想的解决方案:热切地初始化该字段。

private List<String> bone = new ArrayList<>();

下一个解决方案:让它返回Optional并在不存在的情况下执行某些操作。如果你愿意,也可以在这里提供空集合而不是扔你。

Dog dog = new Dog();
dog.get().orElseThrow(new IllegalStateException("Dog has no bones??"));

答案 1 :(得分:1)

因为返回空集合的替代方法通常会返回pkg.interior[indent] pkg.regionFor.keyword(":").append[newLine] pkg.append[newLine] ;然后呼叫者必须添加针对null的警卫。如果返回空集合,则会减轻错误类别。在Java 8+中,还有Optional类型,它可以在没有NullPointerException的情况下实现相同的目的。

答案 2 :(得分:1)

您对此感到困惑的原因是因为您首先没有初始化bone。如果您在构造函数中创建新的List<string>,则会发现get()中的签入是无根据的。

虽然如果您以数学方式处理问题,但您希望为不存在的集合保留返回null值,而不是仅为空的集合。

不存在的集合在编程中没有太多实际应用,但这仍然是思考集合的好方法。

答案 3 :(得分:-1)

我不认为我理解为什么你反对空集合,但我同时指出我认为你的代码需要改进。也许这就是问题?

避免在您自己的代码中进行不必要的null检查:

public class Dog{

   private List<String> bone = new ArrayList<>();

   public List<String> get(){
       return bone;
   }
}

或者考虑不是每次都创建一个新列表:

 public class Dog{

   private List<String> bone;

   public List<String> get(){
       if(bone == null){
        return Collections.EMPTY_LIST;
       }
       return bone;
   }
}

答案 4 :(得分:-1)

以下答案可能会对您的问题感兴趣:should-functions-return-null-or-an-empty-object

总结:

如果您打算表明没有可用的数据,则返回null通常是最佳选择。

空对象意味着返回了数据,而返回null则清楚地表明没有返回任何内容。

此外,如果您尝试访问对象中的成员,则返回null将导致null异常,这对于突出显示错误代码非常有用 - 尝试访问任何成员都没有意义。访问空对象的成员不会失败意味着错误可能无法发现。

同样来自干净的代码:

使用null的问题是使用接口的人不知道null是否是可能的结果,以及他们是否必须检查它,因为没有非null引用类型。< / p>

来自Martin Fowler的特殊案例模式

Null在面向对象程序中是尴尬的东西,因为它们打败了多态。通常,您可以在给定类型的变量引用上自由地调用foo,而无需担心该项是精确类型还是子类。使用强类型语言,您甚至可以让编译器检查调用是否正确。但是,由于变量可以包含null,因此可能会通过调用null上的消息来遇到运行时错误,这将为您提供一个友好的,友好的堆栈跟踪。

如果变量可能为null,则必须记住用空测试代码包围它,这样如果存在null,你就会做正确的事情。在许多情况下,正确的事情通常是相同的,所以你最终会在很多地方编写类似的代码 - 犯下代码重复的罪。

空洞是这类问题的常见例子,其他问题经常出现。在数字系统中,你必须处理无穷大,这对于诸如加法之类的东西有特殊的规则,它打破了实数的通常不变量。我最早的商业软件经验之一是与一位不为人所知的公用事业客户,被称为&#34;乘客。&#34;所有这些都意味着改变了这种类型的通常行为。

返回一个与调用者期望的接口相同的特殊情况,而不是返回null或一些奇数值。

最后来自Billion Dollar Mistake!

我称之为十亿美元的错误。这是1965年空引用的发明。那时,我正在设计第一个面向对象语言(ALGOL W)的引用的综合类型系统。

我的目标是确保所有引用的使用绝对安全,并由编译器自动执行检查。但我无法抵制引入空引用的诱惑,仅仅因为它很容易实现。

这导致了无数的错误,漏洞和系统崩溃,这可能在过去四十年中造成了数十亿美元的痛苦和损害。

近年来,许多程序分析器(如Microsoft中的PREfix和PREfast)已用于检查引用,如果存在风险,则可能会出现非空的警告。最近的编程语言如Spec#引入了非空引用的声明。这是我在1965年拒绝的解决方案。

Tony Hoare

希望这提供足够的理由说明为什么更好地返回空集合或特殊返回值而不是null

相关问题