Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mark method call that it always returns not null result

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.

like image 928
uthark Avatar asked Aug 28 '13 17:08

uthark


1 Answers

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.

like image 108
Mortimer Avatar answered Nov 15 '22 03:11

Mortimer