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())
};