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.