Scala's Option class has an orNull
method, whose signature is shown below.
orNull [A1 >: A](implicit ev : <:<[Null, A1]) : A1
I'm bewildered by the implicit thing. Would somebody please explain how it can be used, ideally with an example?
Scala's Option class has an orNull
method, whose signature is shown below.
orNull [A1 >: A](implicit ev : <:<[Null, A1]) : A1
I'm bewildered by the implicit thing. Would somebody please explain how it can be used, ideally with an example?
To explain the implicit thing: orNull is a way of getting back from the Some|None idiom to Java's value|null idiom (which is, of course, bad). Now only AnyRef values (instances of classes) can accept a null value.
So what we would have liked is
def orNull[A >: Null] = ....
. But A is already set and we don't want to restrict it in the definition of the trait. Therefore, orNull expects an evidence that A is a nullable type. This evidence is in the form of an implicit variable (hence the name 'ev')<:<[Null, A1]
can be written asNull <:< A1
seeing it like this, it is similar to 'Null <: A1'. <:< is defined in Predef as well as the method that provides the implicit value namedconforms
.I think the use of A1 is not strictly required here and is because orNull uses getOrElse (where the default given can be a super type of A)
Re : 'how' is this used - one place we are finding this useful is when dealing with java api mappings where
null
is commonplace, e.g. on jdbc prepared statements to nullable sql columns. TheOption
al internal model fields can be mapped:Instead of the more verbose:
orNull
purpose is first of all in ensuring compatibility ofOption
with Java. Though usage ofnull
is discouraged in Scala, some interfaces may expect to get nullable references.orNull
has a straightforward implementation:According to this,
null
will be returned not only for boxed nulls (Some(null)
), but also forNone
(e.g., if you callNone.get
, exception will be thrown).Implicit parameter checks, if the boxed value is nullable.
Good usage example can be found right in the comments to
orNull
:To understand why it is useful, IttayD provided a nice explanation:
In summary, type constraints are useful when you want have methods (eg
orNull
) on a generic class (egOption
) with more specific constraints (egNull <: A <: Any
) than on the class itself (egA <: Any
).This is another "feature" that is not built into the language but comes for free thanks to implicit parameters and variance annotations of type parameters. To understand this, look at the definition of
<:<
:For
the compiler looks for an implicit value of type
<:<[Null, Int]
and will find the methoddef conforms[A]: A <:< A
. So there has to be anA
for which<:<[A, A]
conforms to<:<[Null, Int]
. There is noA
for which this holds and as a result the compiler will complain about the missing implicit value.However, for
we are lucky. Now, the compiler tries to find an
A
for which<:<[A, A]
conforms to<:<[Null, String]
. This works forA = String
, becauseNull
is a subtype ofString
and theFrom
type parameter of the class<:<
is defined as contravariant).As mentioned, the most intuitive way to think about type constraints is reading it like a type bound (i.e. reading it as Null <: Int).
Null
does not conform toInt
and there is no implicit value for <:<[Null, Int]. On the other hand,Null
does conform toString
and the compiler will find the implicit parameter.By the way, here is another related answer.
Remember that in Scala primitive types and reference types are unified - but only reference types are nullable. The implicit simply allows the compiler to confirm that A1 is a reference type.