9.5. Kowariantne parametry typu

Nie każdy parametr typu ogólnego musi być niezmienniczy. Można poprzedzić parametr znakiem + co oznacza, że jest on kowariantny. Taka sytuacja ma miejsce w przypadku definicji klasy Reader z pliku Reader.scala i oznacza, że w przypadku klas A i rozszerzającej ją B, Reader[A] jest typem nadrzędnym dla Reader[B].

Plik Reader.scala:
class Reader[+T](val content: T) {
  def read: T = content
}

W związku z tym Reader[Int] jest podtypem Reader[Any], a zatem obiekt typu Reader[Int] może być użyty w miejscu, w którym wymagany jest obiekt typu Reader[Any].

scala> val intReader: Reader[Int] = new Reader(1234)
intReader: Reader[Int] = Reader@1d14d74

scala> val anyReader: Reader[Any] = intReader
anyReader: Reader[Any] = Reader@1d14d74

scala> anyReader.read
res0: Any = 1234

Plik CovariantBox.scala przedstawia poprawioną definicję klasy modelującej pudełko z poprzedniego podrozdziału.

Plik CovariantBox.scala:
class CovariantBox[+T](name: String, e: T) { 
  def take: T = {
    println("Taking "+e+" from the "+this)
    e
  }
  override def toString = name
}

Definicja klasy CovariantBox różni się od definicji klasy Box znakiem + poprzedzającym parametr T w nawiasach kwadratowych w wierszu . Taka definicja powoduje, że typ CovariantBox[Paper] jest nadtypem CovariantBox[ColorPaper], a zatem obiekt typu CovariantBox[ColorPaper] może być użyty w miejscu, w którym wymagany jest obiekt typu CovariantBox[Paper].

scala> val yellowPaperBox: CovariantBox[Paper] =
     |   new CovariantBox[ColorPaper]("yellow paper box",
     |                                new ColorPaper("yellow"))
yellowPaperBox: CovariantBox[Paper] = yellow paper box

Plik DrawingChild2.scala zawiera definicję klasy wykorzystującej typ CovariantBox[Paper] do zamodelowania pudełka.

Plik DrawingChild2.scala:
class DrawingChild2(name: String,
  box: CovariantBox[Paper],
  wasteBin: WasteBin[Paper]) {
  def draw {
    val paper = box.take
    println("Drawing on "+paper)
    wasteBin.throwAway(paper)
  }
  override def toString = name
}

Teraz możemy już utworzyć nowy obiekt reprezentujący dziecko rysujące na żółtych kartkach.

scala> val paperWasteBin = new WasteBin[Paper]("paper waste bin")
paperWasteBin: WasteBin[Paper] = paper waste bin

scala> val yellowPaperBox = new CovariantBox[ColorPaper](
     |   "yellow paper box",new ColorPaper("yellow"))
yellowPaperBox: CovariantBox[ColorPaper] = yellow paper box

scala> val john = new DrawingChild2("John",yellowPaperBox,paperWasteBin)
john: DrawingChild2 = John

scala> john.draw
Taking yellow paper from the yellow paper box
Drawing on yellow paper
Throwing away yellow paper into the paper waste bin

Język programowania Scala Wydanie 2. Copyright © Grzegorz Balcerek 2016

Licencja Creative Commons

Ten utwór jest dostępny na licencji Creative Commons Uznanie autorstwa-Na tych samych warunkach 4.0 Międzynarodowe.

Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners.