Home > front end >  What is the empty / null value for a generic type in Scala
What is the empty / null value for a generic type in Scala

Time:11-29

Hi I'm learning Scala recently and I am confused about the null/nothing in Scala, in the following code, I'm implementing a customer linked list with generic type, and practice with variance

Defined my abstract class


/**
 * head = first element of the list
 * tail = remainder of the list
 * isEmpty = is this list empty
 * add(int) => new list with this element added
 * toString => a string representation of the list
 */
abstract class MyList[ T] {
  val head: T
  val tail: MyList[T]
  val isEmpty: Boolean
  def add[A >: T](n: A): MyList[A]
  def printElements: String
  override def toString: String = s"[$printElements]"
}

Two types of node class - Empty and Cons

class Empty[ T] extends MyList[T] {
  override val head: T = ??? // what should I implement at here???
  override val tail: MyList[T] = null
  override val isEmpty: Boolean = true

  override def add[A >: T](elem: A): MyList[A] = new Cons(elem, this)
  override def printElements: String = ""
}

class Cons[ T](h: T, t: MyList[T]) extends MyList[T] {
  override val head: T = h
  override val tail: MyList[T] = t
  override val isEmpty: Boolean = false

  override def add[A >: T](elem: A): MyList[A] = new Cons(elem, this)
  override def printElements: String = {
    if (tail.isEmpty) s"$head"
    else s"$head, ${tail.printElements}"
  }
}

Here is my test

object ListTest2 extends App {
  val listOfIntegers: MyList[Int] = new Cons(1, new Cons(2, new Empty[Int]))
  val listOfString: MyList[String] = new Cons("1", new Cons("2", new Empty[String]))

  println(listOfIntegers)
  println(listOfString)
}

I tried to define an object Empty for the last node in this linked list.

//object Empty extends MyList[Nothing] {
//  override val head: Nothing = throw new NoSuchElementException
//  override val tail: MyList[Nothing] = null
//  override val isEmpty: Boolean = true
//
//  override def add[B >: Nothing](elem: B): MyList[B] = new Cons(elem, Empty)
//  override def printElements: String = ""
//}

But it run with exception during the initiation.

CodePudding user response:

Returning null from tail is not so good idea. This can lead to NPE. It's better to fail early, so just throw (if this is your semantics of the method). Better semantics is to return an empty list.

head also can throw. This is the only way to create a value of arbitrary type T. If you can change the signature then it's better to return Option[T] i.e. None instead of null or exception.

Normally, in a trait or abstract class, abstract members (head, tail, isEmpty) should be def. Whether they are actually def, val or lazy val is implementation details (and should be specified in inheritors).

I tried to define an object Empty for the last node in this linked list... But it run with exception during the initiation.

You can avoid exception if you make head a lazy val (or def)

override lazy val head: Nothing = throw new NoSuchElementException

def can be overridden with def, val, lazy val.

  • Related