1. Overview

In this tutorial, we’ll show the differences between Nil, Null, Nothing, Unit, and None types in Scala.

Although it may seem like all these keywords are used for representing the absence of a value, each one has its own purpose.

We’ll go through each of them and learn its characteristics using examples and use cases.

2. Null and null

The null reference is used to represent an absent value, and Null with a capital ‘N’ is its type.

2.1. null Reference

Let’s first start with the null reference, as it’s probably the most familiar one for most of us because of the dreaded NullPointerException it causes.

null is the value of a reference that is not referring to any object. It can be used as a replacement for all reference types — that is, all types that extend scala.AnyRef.

Let’s look at an example of initializing a class with a null reference:

case class Car(make:String)

//Initializes an instance of Car with null reference
val nullRefCar:Car = null
try{
  println(nullRefCar.make)
}catch{
  case npe:NullPointerException => println("Null Pointer Error occurred: %s".format(npe))
}

//Initializes an instance of Car type with argument as null
val nullMakeCar = Car(null)
println(nullMakeCar.make)

We must try to avoid using null while initializing variables if there’s an empty value of the variable’s type available, such as Nil.

Also, it’s advisable to use Option types for functions that may return an empty result, instead of returning a null reference.

2.2. Null Type

Null is the type of the null reference. It extends all reference types including the custom classes and traits we define. This allows us to use the null value in place of any reference type.

The Null type doesn’t have any methods or fields. It’s defined as an abstract final class, therefore, it doesn’t allow to be extended or instantiated. null is the one and the only instance of type Null.

3. Nil – the Empty List

Nil is an empty singleton object list that extends the List type, therefore, it has all fields and methods that any other List object has, and it’s usually used to initialize empty lists:

val myList = Nil
println("a list is initialized with length %s".format(myList.length))

All List methods are applicable for Nil as well. Thus, it can be used safely in place of any regular List. A popular way of creating and populating new lists is using Nil and the cons (::) operator:

val consList = "A" :: "B" :: Nil

4. None – the Empty Option

None is a subtype of Option type. We’ve seen the problems caused when functions return null values in case they don’t have any values to return. This may cause calling programs to crash if it doesn’t properly handle null.

Scala’s best practices advise us to wrap the return value in the Option type in cases where the function may not have a return value.

Let’s look at an example of a function to get the name of the student by passing a roll-number:

val studentRegister:Map[Int,String] = Map(1 -> "John", 2 -> "Mary")

def getStudentName(studentRegister:Map[Int,String], roll:Int):Option[String] = {
  studentRegister.get(roll)
}

def printStudent(student:Option[String]): Unit = {
  student match {
    case Some(str) => println("Student Name is %s".format(str))
    case None      => println("No Student!!")
  }
}

In case the roll number is not available in the register, the function returns a None and the calling method can gracefully handle the case of a missing record.

5. Unit – the Empty Return Type

Unit is the return type of functions returning nothing. It’s equivalent to the void type in Java except for one difference: The Unit type in Scala has one singleton value that is (), but void in Java does not have any value:

def functionReturnUnit:Unit = {
  """
    do something, don't return anything
  """
}
println("result of function returning Unit: %s".format(functionReturnUnit))

We’ll see an output printed in the console as “result of function returning Unit: ()”.

If we omit the return type and the “=” operator in the function definition, the Scala compiler will implicitly convert it to a Unit return type:

def functionReturnImplicitUnit {
  """
    do something
  """
}

6. Nothing

Nothing is the absolute “no value” type in Scala. It doesn’t have any methods or values.

Any type is the root of the entire Scala type system, and Nothing extends the Any type. Therefore, we can use Nothing in place of any Scala type both reference types and value types.

Nothing together with the Null type sits at the bottom of the type hierarchy. Therefore, it’s also a subtype of every other type in Scala — even the classes and traits we define. This property gives us a lot of advantages in situations such as defining a generic empty base class. Nil is such an example: Nil extends List[Nothing], thus allows it to be used with a List of any type, be it String, Int, or custom classes.

We can’t use Nothing as the return type of a function except in the case where the function throws an exception. This is because Nothing doesn’t have any value. One such use case is a function that logs an exception and throws another custom exception:

def logException(e:Exception):Nothing = {
  println("logging Exception: %s".format(e.getMessage))
  throw new Exception("My New Exception")
}

7. Conclusion

In this tutorial, we looked at the six entities for representing nothingness in Scala with the help of a series of simple examples. We learned about the characteristics and behavior of each of them and their applications. Then, we concluded by understanding some of the best practices such as using Option in place of returning null and initializing a List using Nil.

As always, the full source code of the article is available over on GitHub.

Comments are closed on this article!