(Linked)BlockingQueue.put(null) 抛出 NullPointerException。

13

我已经检查了这个实现,它是有意为之的。

 public void put(E e) throws InterruptedException {
     if (e == null) throw new NullPointerException();

这种惊喜对用户来说不方便(例如,想要以此方式发出流结束信号的用户),并破坏了与集合的一般契约,后者很容易接受空元素。BlockingQueue区分空元素的意义何在?如果null值如此糟糕,我们可能应该完全避免使用它们,并在JLS中执行此操作吗?

1个回答

14

接受null值并非Collection合同的一部分。实际上,Collection的Javadoc明确指出:

某些集合实现对它们所包含的元素有限制。例如,某些实现禁止空元素,而有些则对其元素类型有限制。尝试添加不合格的元素会抛出未检查的异常,通常是NullPointerException或ClassCastException。

在许多情况下,向集合中添加null意味着您的程序中存在错误,而不是故意放置它。例如,我贡献代码的Guava库做出了明确的决定,拒绝在许多集合实现中包含null值,特别是不可变的集合实现:

我们对Google内部代码库进行了详尽的研究,发现空元素在约5%的情况下允许存在于集合中,而其他95%的情况最好快速失败以避免null值。

通常有一些解决方法可以接受空值,但是许多集合实现决定拒绝空值(这对大多数用户来说很有帮助,因为它们能够帮助他们找到错误),并为明确的空值提供解决方法,以应对罕见情况。

老实说,我认为LinkedBlockingQueue之所以属于这个类别,是因为当最初开发集合框架时还没有想清楚这一点,但是在添加并发集合时已经非常明确了。Doug Lea是util.concurrent的主要开发者之一,他曾经说过:

空值很糟糕。

在最坏的情况下,对象包装器或“毒药对象”始终是有效的解决方法;Guava提供了一个Optional类,在许多情况下可以担任该角色,这在StackOverflow上得到了广泛讨论here


1
好的,BlockingQueue有什么特别之处需要阻止null值吗?如果没有,那为什么其他集合不这样做呢? - Val
2
问题在于 null 无法完全禁止。除了完全破坏向后兼容性(即使这意味着在 Java 中保留明显的错误和不良库,Sun 和 Oracle 都非常小心),例如在对象数组上,你无法避免它:当你说 new String[5] 时,如果不是 null,那么数组会填充什么?这很不愉快,但即使我们想要摆脱它,也不清楚我们能否做到。 - Louis Wasserman
1
不在集合中,当然,但我们不能从Java语言中删除数组。此外,许多集合实现在内部使用数组。尽管如此,在JVM上运行的语言确实禁止null。即使我们无法在Java中更改它,其他语言也可以并且确实禁止null。 - Louis Wasserman
1
数组可以使用默认值进行初始化,对于字符串来说,它可以是""。但即使无法这样做,也不能以此为借口不告知人们每次使用null都是应用程序中存在错误的迹象。然而,我认为null本身并不是坏的。它们只是与错误相关,因为有许多初始化错误。但任何东西都可以被用于恶意。我们不应该停止使用变量初始化,仅仅因为它可能会出现错误。同样的道理,我们也不应该停止使用null。 - Val
2
@Val FWIW 四年后 - “BlockingQueue 有什么特别之处需要阻止 null 呢?”嗯,首先 poll(long timeout, TimeUnit unit) 使用 null 来表示超时发生。如果该类代替此目的使用 TimeoutException,允许 null 值可能是可以的,但事实并非如此。 - JHH
显示剩余11条评论

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