13

I have checked the implementation, it does so intentionally

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

This surprise is not convenient for user (who wants to signal end of stream this way, for instance) and breaks the general contract with collections, which easily accept the null elements. What is the point of BlockingQueue to discriminate null elements? If nulls are so bad, might be we should refrain using them at all and enforce this low in JLS?

Val
  • 1
  • 8
  • 40
  • 64

1 Answers1

14

Accepting nulls is not part of the Collection contract. Indeed, the Collection Javadoc specifically states:

Some collection implementations have restrictions on the elements that they may contain. For example, some implementations prohibit null elements, and some have restrictions on the types of their elements. Attempting to add an ineligible element throws an unchecked exception, typically NullPointerException or ClassCastException.

In many cases, adding null to a collection means there's a bug in your program somewhere, not that you put it in deliberately. For example, the Guava library (which I contribute to) makes the explicit decision to reject nulls from many of their collection implementations, specifically the immutable ones:

We did an exhaustive study on Google's internal code base that indicated that null elements were allowed in collections about 5% of the time, and the other 95% of cases were best served by failing fast on nulls.

There are generally workarounds that do accept nulls, but many collection implementations make the decision to reject nulls (which most users find helpful, as it helps them find bugs) and offer workarounds for the rare case where explicit nulls are appropriate.

In all honesty, I think the reason that LinkedBlockingQueue is in this category is that all this hadn't been figured out when the original collections framework was developed, but it was pretty clear by the time that the concurrent collections were being added. Doug Lea, who did much of the work on util.concurrent, has been quoted as saying,

Null sucks.

In the worst case, object wrappers or "poison objects" are always valid workarounds; Guava provides an Optional class which can serve that role in many cases, which is discussed extensively here on StackOverflow.

Community
  • 1
  • 1
Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • 1
    Ok, what is so special in BlockingQueue that needs it to block the nulls? If nothing, then why other collections do not do that? – Val May 31 '12 at 09:18
  • You say "In many cases, adding null to a collection means there's a bug in your program somewhere". I suspect that nulls appear in collections, read from local variables, which have null values. ANy null value in the program signals a bug. That is why propose to prohibit nulls completely, in JLS. – Val May 31 '12 at 09:20
  • 2
    The thing is that nulls can't be completely banned. Besides breaking backwards compatibility completely (which Sun and Oracle have been _extraordinarily_ careful not to do, even when it means keeping obvious mistakes and bad libraries in Java), you can't avoid it for example on object arrays: when you say `new String[5]`, what else would the array be filled with if not `null`? It's unpleasant, but it's not clear that we could get rid of it even if we wanted to. – Louis Wasserman May 31 '12 at 09:23
  • I do not care about String[5] for the reason BlocingQueue and Google do not care about user working around prohibited null in their collection. – Val May 31 '12 at 09:25
  • 1
    Not in collections, sure, but we can't exactly delete _arrays_ from the Java language. Besides, many collection implementations use arrays internally. All that said, there are [languages](http://confluence.jetbrains.net/display/Kotlin/Null-safety) that run on the JVM that do, indeed, forbid `null`. Even if we can't change it in Java, other languages can, and do, forbid null. – Louis Wasserman May 31 '12 at 09:26
  • 1
    Arrays can be initialized with default values, it could be "" for the String. But even if you could not, it is not an excuse for not informing people that every use of null is a sign of bug in their app. However, I think that nulls are not bad for themselves. They just correlate with bugs because there are many initialization errors. But any thing can be used for evil. We should not stop using variable initialization just because it can be buggy. We should not stop using nulls for the same reason. – Val May 31 '12 at 09:34
  • Perhaps `""` for `String`, but what about types that don't have that kind of "sensible default"? There _are_ some cases where you just can't avoid `null` even if you wanted to. But...as it stands, many of the collections try to help you avoid `null`, but provide slightly less obvious workarounds when you actually, really do want to avoid `null`. I think that's the right call; it more or less helps you "make sure you really want nulls." Libraries that make it easy for users to shoot themselves in the foot accidentally...aren't always the best libraries. – Louis Wasserman May 31 '12 at 09:36
  • 1
    I admitted that there are cases where you cannot. And added that this is not an excuse to forbid nulls in collections. I'm not satisfied with stupid rules that preclude us doing right things. Everything can be used for bad. This does not mean that nulls must be forbidden. How much code is buggy just because it adds wrong (but not null) elements into the list? Might be we should throw an exception if user tries to use a collection an any way? – Val May 31 '12 at 09:45
  • But thanks for exposing the util.concurrent designer's line of thought. – Val May 31 '12 at 09:53
  • 1
    Nobody's forbidding anything. Library developers are just making it _slightly more difficult_ to e.g. use nulls, so you don't shoot yourself in the foot without meaning to, but there are still workarounds. (I don't know about you, but I'm happy I'm working in a language that makes it difficult or impossible to segfault, even if experts in C or C++ can write programs that usually don't segfault; it's more or less the same principle.) – Louis Wasserman May 31 '12 at 09:58
  • public void put(E e) throws InterruptedException { if (e == null) throw new NullPointerException(); --- Sorely, "nulls are not forbidden here at all, you just need to use a tiny workaround". Might be you should not stand you your foot without a hat, because you may fall. – Val May 31 '12 at 10:22
  • Yes, nulls are forbidden in LinkedBlockingQueue. But it's not forbidden in other queue implementations; you can write wrappers to `LinkedBlockingQueue` that do accept nulls (probably wrapping `null` values internally); you can use alternatives that "mimic" nulls like `Optional`. All of those are perfectly legitimate workarounds that let you do the same things you would do if `null` were allowed. – Louis Wasserman May 31 '12 at 10:30
  • http://wouter.coekaerts.be/2012/puzzle-chicken-solution There is a prove that you always can get away without nulls, with just a little ingeniousness :) – Val Jun 02 '12 at 13:24
  • ...Are you _actually_ suggesting that solution for real code? That's the dirtiest hack I've ever seen. – Louis Wasserman Jun 02 '12 at 13:29
  • I think the reason it forbids the null is described on the javadoc: A BlockingQueue does not accept null elements. Implementations throw NullPointerException on attempts to add, put or offer a null. A null is used as a sentinel value to indicate failure of poll operations. – monn Dec 01 '14 at 19:29
  • 2
    @Val FWIW four years later - "what is so special in BlockingQueue that needs it to block the nulls?". Well, for one thing `poll(long timeout, TimeUnit unit)` uses `null` to signal that a timeout occured. Had the class used `TimeoutException` for this purpose instead, allowing `null` values would probably have been fine, but that is not the case. – JHH Mar 09 '16 at 08:23