This is a follow-up to Make Null a subclass of AnyVal under -Yexplicit-nulls - #56 by sjrd . A particular concern that was brought up is that, even under explicit-nulls, the methods of Any are still null-unsafe.
To be clear: all of this thread is under the context of -Yexplicit-nulls. I do not propose to change anything in the currently standard Scala.
Null <: Any in Scala. This is unavoidable, because, by definition, Any is the supertype of all proper types. Proper types are the types of values, as opposed to higher-kinded types, which are the types of other types.
That poses a problem, because several methods available on Any are not supported by null. They throw a NullPointerException. The most obvious example is toString(). Here is a complete table of the methods we are allowed to call on x: Any:
Method of Any |
Behavior for null |
|---|---|
==, != |
null == null and to nothing else) |
## |
0 |
isInstanceOf[X] |
false for all X |
asInstanceOf[X] |
|
hashCode() |
|
equals(y) |
|
toString() |
|
getClass() |
|
nn |
x.hashCode() and x.equals(y) have had good alternatives forever: use x.## and x == y instead.
x.toString() sort of has alternatives: s"$x", "" + x, String.valueOf(x) all return the string "null" when x == null. They’re not very nice, though.
x.getClass() has no alternative. You need a type test.
Proposal
I propose the following changes:
- Deprecate
Any.hashCode()andAny.equals()altogether. Override them as non-deprecated inAnyRef(akajl.Object) - Change the behavior of
Any.toString()andAny.getClass():null.toString()should return the string"null"null.getClass()should returnclassOf[Null]
We can implement the changes using additional dispatch introduced by the compiler. For x: T with Null <: T, we will compile
| Source | Rewritten |
|---|---|
x.toString() |
String.valueOf(x) |
x.getClass() |
if (x ne null) x.asInstanceOf[AnyRef].getClass() else classOf[Null] |
This way, it will be safe to call all (non-deprecated) methods of Any on null. Problem solved.
Prior art
I am told Kotlin allows to call x.toString() when x: Any?, and that it returns "null". Scala’s Any type is equivalent to Kotlin’s Any?: they each are the supertype of all proper types in their respective type systems.
For getClass(), it’s a bit convoluted, but since we can write classOf[Null] in source and do get an actual instance of jl.Class, I believe that is the natural choice.
WDYT?