JavaNullInterop
This module defines methods to interpret types of Java symbols, which are implicitly nullable in Java, as Scala types, which are explicitly nullable.
The transformation is (conceptually) a function n
that adheres to the following rules:
(1) n(T) = T | Null if T is a reference type
(2) n(T) = T if T is a value type
(3) n(C[T]) = C[T] | Null if C is Java-defined
(4) n(C[T]) = C[n(T)] | Null if C is Scala-defined
(5) n(A|B) = n(A) | n(B) | Null
(6) n(A&B) = n(A) & n(B)
(7) n((A1, ..., Am)R) = (n(A1), ..., n(Am))n(R) for a method with arguments (A1, ..., Am) and return type R
(8) n(T) = T otherwise
Treatment of generics (rules 3 and 4):
- if
C
is Java-defined, thenn(C[T]) = C[T] | Null
. That is, we don't recurse on the type argument, and only add Null on the outside. This is becauseC
itself will be nullified, and in particular so will be usages ofC
's type argument within C's body. e.g. callingget
on ajava.util.List[String]
already returnsString|Null
and notString
, so we don't need to writejava.util.List[String | Null]
. - if
C
is Scala-defined, however, then we wantn(C[T]) = C[n(T)] | Null
. This is becauseC
won't be nullified, so we need to indicate that its type argument is nullable.
Notice that since the transformation is only applied to types attached to Java symbols, it doesn't need to handle the full spectrum of Scala types. Additionally, some kinds of symbols like constructors and enum instances get special treatment.
Value members
Concrete methods
Transforms the type tp
of Java member sym
to be explicitly nullable.
tp
is needed because the type inside sym
might not be set when this method is called.
Transforms the type tp
of Java member sym
to be explicitly nullable.
tp
is needed because the type inside sym
might not be set when this method is called.
e.g. given a Java method String foo(String arg) { return arg; }
After calling nullifyMember
, Scala will see the method as
def foo(arg: String | Null): String | Null
If unsafeNulls is enabled, we can select on the return of foo
:
val len = foo("hello").length
But the selection can throw an NPE if the returned value is null
.