There are lots of terms to express the lack of values in Scala. Today we're going to discuss the difference between null, Null (not a typo), None, Nil, Unit and Nothing. They all seemingly describe the lack of a meaningful value, which can definitely be confusing. Thank goodness Scala doesn't have
This article is for the beginner Scala programmer. We'll make a clear distinction between all these terms, and by the end of this article you'll know which to use when.
The null reference
This is probably the most familiar. If you come to Scala from another programming language, perhaps very quickly, you're probably familiar with the
nullreference. It's used as an absent value.
val anAbsentString: String = null
You've surely dealt with your own set of null-access exceptions. That is because an absent value doesn't have fields or methods.
anAbsentString.length() // triggers the famous NullPointerException
This one is easy.
The Null type
Now, in Scala, the null reference belongs to its own distinct type, which is called
Nullwith a capital N.
val theNullReference: Null = null
The Null type has no methods, no fields, cannot be extended or instantiated, so by itself is pretty boring. The only interesting aspect of Null is that it "extends" all reference types. By that we mean that we can successfully use it as a replacement for any reference type
val noString: String = theNullReference val noPerson: Person = theNullReference val noList: List[Int] = theNullReference
So from the point of view of subtyping, Null is a proper subtype of all reference types. In Scala, the reference type hierarchy starts with AnyRef at the top and ends with Null at the bottom.
A common question is: how can Null be a proper subtype for all reference types, since Scala offers a single-class inheritance model? The answer is that Null is treated in a particular way by the compiler, so there's no need for us programmers to intervene in any way.
This sounds very similar to null and Null, but Nil means something completely different: Nil is the empty implementation of a List.
val anEmptyList: List[Int] = Nil
Unlike null, Nil is a proper value. It has fields and methods:
val emptyListLength: Int = Nil.length
We can pass it around:
def processList(list: List[Int]): Int = list.length // later: procesesList(Nil)
and generally do with Nil whatever we do with regular values.
Truth be told, we very rarely use
nullin Scala. Using null incentivizes us to write imperative, Java-style, defensive and error-prone code. Instead of
null, we commonly use Options, which are data structures meant to represent the presence or absence of a value. Options allow (and force) us to write clearer, more concise code which is harder to fail. The two kinds of instances we can use for Option are
Someinstances and the
val anAbsentInt: Option[Int] = None val aPresentInt: Option[Int] = Some(42)
The difference between
Noneis a proper value (much like
Nil) and we can pass it around and process it. We're going to talk more about Options and why they are useful in another article.
Nonevalues are interoperable, in the sense that we can lift ourselves from the muddy null-checking realm to Options by using the Option apply factory method:
val anAbsentValue: Option[Int] = Option(null) // this returns None
Thankfully, Unit will hopefully prove a little clearer. One of the very first things we learn as Scala programmers coming from another language is how to declare methods returning "void". The equivalent of "void" in other languages is
def aUnitReturningFunction(): Unit = println("Starting to get the difference!")
If you are clear on the difference between
voidin other languages, you'll understand how
Unitis different from what we talked so far.
Finally, let's end with the mother of nothingness. We've spoken about Nothing before so we won't spend too much time here, but Nothing is the type of no value at all. Nothing can't be instantiated and has no values of that type. Nothing truly means nothing: not even null, not Unit, not None, nothing at all. The only expressions that return Nothing are throwing exceptions:
val theNothing = throw new RuntimeException("Nothing to see here")
The interesting thing about Nothing is that, much like Null, it can be used as a replacement for any type - this time including the value types:
val nothingInt: Int = throw new RuntimeException("No int") val nothingString: String = throw new RuntimeException("No string")
In other words, Nothing is a proper subtype for all possible types in Scala. Much like Null, it's treated in a special way by the compiler.