2

I am new to Scala. I have question about List class. This is an abstract and sealed class. It means cannot be instantiated, neither extended. So what is its usage? How can it be that something like following works?

val myList = List(1,2,3)

Would myList be a reference to the List object isntance? Also, if productElement is an abstract method how can that be implemented?

thanks.

toto'
  • 1,325
  • 1
  • 17
  • 36

2 Answers2

3

sealed means it cannot be extended outside the file where it is defined. List is sealed but it does have 2 non-abstract sub-classes defined in the same file: Nil and :: representing an empty list an a non-empty list respectively.

When you call List(1,2,3) you're not really instantiating the List class directly. It is syntax sugar for List.apply(1, 2, 3) which is calling the apply method on the List object (companion to the List class). And that method is (technically through some indirection) eventually producing an instance of either Nil or ::.

You can see the list sub-classes here: https://github.com/scala/scala/blob/2.13.x/src/library/scala/collection/immutable/List.scala#L547 https://github.com/scala/scala/blob/2.13.x/src/library/scala/collection/immutable/List.scala#L554

You don't need to implement productElement it is created by the scala compiler for all case classes.

Alvaro Carrasco
  • 6,103
  • 16
  • 24
  • thanks, clear. And what about how to implement its abstract methods like productElement ? – toto' Jun 24 '18 at 15:50
2

Regarding:

val myList = List(1,2,3)

When the compiler encounters an expression which creates a class instance without the new modifier, it looks up that classes companion object to look for a .apply method. In the case of list, it is defined as:

override def apply[A](xs: A*): List[A] = xs.toList

Thus, this compiles successfully. You can view this when asking the compiler to emit type information after the typer phase:

def main(args: Array[String]): Unit = {
  val l: List[Int] = scala.collection.immutable.List.apply[Int](1, 2, 3);
  ()
}

Would myList be a reference to the List object isntance?

The run-time type of myList would be either a cons (::) or the empty list (Nil).

if productElement is an abstract method how can that be implemented?

This is a compiler trick. productElement and productArity (and more) are both generated at compile time for any case class definition. For example, given the following case class:

case class Bar(i: Int)

The compiler generates:

// an incomplete view of the generated case class methods and fields
// omitted for brevity.
case class Bar extends AnyRef with Product with Serializable {
  <caseaccessor> <paramaccessor> private[this] val i: Int = _;
  <stable> <caseaccessor> <accessor> <paramaccessor> def i: Int = Bar.this.i;
  def <init>(i: Int): yuval.tests.Foo.Bar = {
    Bar.super.<init>();
    ()
  };
  <synthetic> def copy(i: Int = i): yuval.tests.Foo.Bar = new Bar(i);
  <synthetic> def copy$default$1: Int = Bar.this.i;
  override <synthetic> def productPrefix: String = "Bar";

  // this is the relevant part to your question
  <synthetic> def productArity: Int = 1;
  <synthetic> def productElement(x$1: Int): Any = x$1 match {
    case 0 => Bar.this.i
    case _ => throw new IndexOutOfBoundsException(x$1.toString())
  };
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321