Frage

Scala compiler has -Xcheck-null which tries to check if there are any potential null pointer dereference in runtime.

It's ok for me, but I get too much false positives, i.e. suppose I define logger :

private final val LOGGER: Logger = LoggerFactory.getLogger(classOf[GenericRestImpl])

The method getLogger never returns null. How can I pass this knowledge to compiler so it will not complain?

[WARNING] TestImpl.scala:31: warning: potential null pointer dereference: LOGGER.debug
[WARNING]       LOGGER.debug("Using {} for sort", sortParam)

When I create new instance I can mark it with NotNull trait:

return new Foo() with NotNull.

That's ok, but what to do with objects returned from other methods? Especially if it is comming from 3rd party library? I don't like idea to mark all my variables as Optional, because it will add too much overhead. Also, I don't like idea to create implicit conversions (because it will require extra class for each class that I want to mark as NotNull.

I also checked question Library support for Scala's NotNull trait but it didn't help to solve my problem.

War es hilfreich?

Lösung

As Jatin mentions, NotNull is just a marker or tag, so you can use NotNull to tag anything. The trick to do this is to forcing a cast your base type with NotNull.

So you can write something like this "notnull".asInstanceOf[String with NotNull]. It's a safe cast if you are sure it's never ever null.

In your actual example, you can therefore write:

private final val LOGGER: Logger with NotNull = 
   LoggerFactory.getLogger(classOf[GenericRestImpl]).asInstanceOf[Logger with NotNull]

While there is no need to create new types for this, it's a bit cumbersome if you have to do it a lot, so you could use some little utils to simplify/clarify the notation:

type NeverNull[T] = T with NotNull
def neverNull[A](a: A): NeverNull[A] = a.asInstanceOf[A with NotNull]

NeverNull is just an alias for any type T tagged with NotNull and neverNull is a little wrapper to tag any existing value of type A as being never null.

You can then use it as:

private final val LOGGER: NeverNull[Logger] = neverNull {
       LoggerFactory.getLogger(classOf[GenericRestImpl])
}

You could even make this an implicit conversion, if you are really sure of what you are doing:

implicit def neverNull[A](a: A): NeverNull[A] = a.asInstanceOf[A with NotNull]

private final val LOGGER: NeverNull[Logger] = LoggerFactory.getLogger(classOf[GenericRestImpl])

Note that NeverNull[Logger] is still a Logger, so you can call any method of that class on it, or pass it on to functions that take as parameter a Logger.

This kind of construct is called an unboxed tagged type and is pretty useful, see other applications and discussion here and here.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top