Type Parameterization

Type parameterization allows you to write generic classes and traits. For example, sets are generic and take a type parameter: they are defined as Set[T]. As a result, any particular set instance might be a Set[String], a Set[Int], etc.—but it must be a set of something.
Functional queues
head returns the first element of the queue
tail returns a queue without its first element
enqueue returns a new queue with a given element appended at the end
scala> val q = Queue(1, 2, 3)
q: Queue[Int] = Queue(1, 2, 3)

scala> val q1 = q enqueue 4
q1: Queue[Int] = Queue(1, 2, 3, 4)

scala> q
res0: Queue[Int] = Queue(1, 2, 3)
Information hiding with private constructors
It is still possible to hide the primary constructor by adding aprivatemodifier in front of the class parameter list.
class Queue[T] private (
          private val leading: List[T],
          private val trailing: List[T]
The private modifier between the class name and its parameters indi- cates that the constructor of Queue is private: it can be accessed only from within the class itself and its companion object. The class name Queue is still public, so you can use it as a type, but you cannot call its constructor:
scala> new Queue(List(1, 2), List(3))
<console>:6: error: constructor Queue cannot be accessed in object $iw
Now that the primary constructor of class Queue can no longer be called from client code, there needs to be some other way to create new queues. One possibility is to add an auxiliary constructor, like this:
def this() = this(Nil, Nil)
The auxiliary constructor shown in the previous example builds an empty queue. As a refinement, the auxiliary constructor could take a list of initial queue elements:
def this(elems: T*) = this(elems.toList, Nil)
T* is the notation for repeated parameters.
Another possibility is to add a factory method that builds a queue from such a sequence of initial elements. A neat way to do this is to define an object Queue that has the same name as the class being defined and contains an apply method:
object Queue {
     // constructs a queue with initial elements ‘xs’
     def apply[T](xs: T*) = new Queue[T](xs.toList, Nil)
Variance Annotations
Prefixing a formal type parameter with a+indicates that subtyping is co- variant (flexible) in that parameter. By adding this single character, you are telling Scala that you wantQueue[String], for example, to be considered a subtype ofQueue[AnyRef]. The compiler will check thatQueueis defined in a way that this subtyping is sound.
Besides +, there is also a prefix -, which indicates contravariant sub-typing. IfQueuewere defined like this:
trait Queue[-T] { … }
then ifTis a subtype of typeS, this would imply thatQueue[S]is a sub- type ofQueue[T](which in the case of queues would be rather surprising!). Whether a type parameter is covariant, contravariant, or nonvariant is called the parameter’svariance. The+and-symbols you can place next to type parameters are calledvariance annotations.
Lower bounds
You can generalize enqueue by making it polymorphic (i.e., giving the enqueue method itself a type pa- rameter) and using a lower bound for its type parameter:
class Queue[+T] (private val leading: List[T], private val trailing: List[T] ) {
    def enqueue[U >: T](x: U) =
      new Queue[U](leading, x :: trailing) // ...
The new definition gives enqueue a type parameter U, and with the syntax, “U >: T”, defines T as the lower bound for U. As a result, U is required to be a supertype of T.1 The parameter to enqueue is now of type U instead of type T, and the return value of the method is now Queue[U] instead of Queue[T]. As an example, suppose there is a class Fruit with two subclasses, Apple and Orange. With the new definition of class Queue, it is possible to append an Orange to a Queue[Apple]. The result will be a Queue[Fruit].
Liskov Substitution Principle
It is safe to assume that a typeTis a subtype of a typeUif you can sub- stitute a value of type T wherever a value of type U is required.