In Chapter 22 of "Programming in Scala" book, the ::
class (cons) is defined as
final case class ::[T](hd: T, tl: List[T]) extends List[T] {
//...
}
The ::
method in class List
is defined as follows:
def ::[U >: T](x: U): List[U] = new scala.::(x, this)
Why is the new
required to create an instance of the final
case
class ::
? Is this purely for disambiguation?
If the class in Scala is final then it cannot inherit to derived class. Inheritance restriction will be added by final keyword. Here if class Shapes are final then its all members also final and cannot used in derived class.
Defining a case class Notice how the keyword new was not used to instantiate the Book case class. This is because case classes have an apply method by default which takes care of object construction. When you create a case class with parameters, the parameters are public val s. You can't reassign message1.
case classes automatically have equality and nice toString methods based on the constructor arguments. case classes can have methods just like normal classes.
Case classes can't be extended via subclassing. Or rather, the sub-class of a case class cannot be a case class itself. Final prevents extending a case class with anything ( i.e. case and not case classes), in addition to the built-in restriction to extend case classes with case classes.
With case classes you automatically get a companion object whose apply
method calls the constructor, in the same way as you can do this with an ordinary class:
class Foo(val value: Int)
object Foo { def apply(value: Int) = new Foo(value) }
val x = new Foo(42) //
val y = Foo(42) // both work the same
You can instantiate case classes with new
if you want to. It might in theory be slightly faster because it doesn't have to go via the companion object's apply
method, but I tried a quick benchmark and saw absolutely no difference in performance, so I guess it's optimised by the compiler, or just an immeasurably small difference compared to the actual construction.
So I don't think the new
in the example you give has any significance and could just as well have been left out.
You are correct; the new
is not mandatory. They could have defined the instance method List#::
like this just as well:
def ::[U >: T](x: U): List[U] = scala.::(x, this)
(Note that we have:
type :: = collection.immutable.::
val :: = collection.immutable.::
defined in the scala
package object; the first is why your new scala.::(x, this)
works, and the second is why my scala.::(x, this)
works.)
The form the library uses calls the constructor directly, as yours does. The alternative calls the apply
method of the synthetic companion object generated for the ::
case class, which simply calls the constructor anyway. Perhaps calling the constructor was deemed clearer, or more efficient? (Efficiency gains should be close to nothing, though, since if the compiler doesn't inline the call to apply
, the JVM will.) I suppose the most compact form:
def ::[U >: T](x: U) = ::(x, this)
could be mistaken for some wacky (i.e., impossible) sort of recursive invocation, and at any rate blurs the distinction between the class called ::
and the List
method called ::
, which Prof. Odersky takes pains to keep separate in order to maximize reader comprehension.
Hope this helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With