I deal a lot with Java’s concurrent collections, including their
null-returning methods, like
poll. Consider this (non-concurrent) example:
val q = ju.concurrent.ConcurrentLinkedQueue[Int]() val a: Int = q.poll() // 0 val b: Any = q.poll() // null // q.poll() == null // rejected // null == q.poll() // rejected // null eq q.poll() // rejected val c = q.poll() eq null // false
My first question is: Is this the expected behavior? It all seems pretty dangerous. I would have expected the evaluation of
a to cause an
NPE, and the test
q.poll() eq null to be true. As it is, I worry about differentiating an empty queue from a queue that starts with 0.
My second question is: What are the safe usage patterns?
Option and pattern-matching seem to be fine, but I’m still nervous:
val d: Option[Int] = Option(q.poll()) // None q.add(0) val e: Option[Int] = Option(q.poll()) // Some(0) def drain[A](q: ju.Queue[A]): List[A] = q.poll() match case null => Nil case value => value :: drain(q) q.add(0) val f = drain(q) // List(0)
I wouldn’t want to hit a weird case where, say,
Option(q.poll()) evaluates to
Some(0) on an empty queue, or to
None on a queue that starts with 0…