http://docs.scala-lang.org/tour/lower-type-bounds.html中有一段代碼html
trait Node[+B] {
def prepend(elem: B): Unit
}
case class ListNode[+B](h: B, t: Node[B]) extends Node[B] {
def prepend(elem: B) = ListNode[B](elem, this)
def head: B = h
def tail = t
}
case class Nil[+B]() extends Node[B] {
def prepend(elem: B) = ListNode[B](elem, this)
}
文中說這段代碼不會經過編譯,由於Function1是contravariant 在參數的位置上。看到這裏是一個頭不少個大的。 However, this program does not compile because the parameter elem
in prepend
is of type B
, which we declared covariant. This doesn’t work because functions are contravariant in their parameter types and covariant in their result types.java
先假設一下若是能編譯經過的話。this
假設有這樣子的一段代碼spa
trait Animal
case class Dog() extends Animal
case class Cat() extends Animal
def addDogToAnimal(animalNode : ListNode[Animal]) : Unit{
animalNode.prepend(Dog()) }
若是generic的類型是Animal的話,ListNode就變成以下
case class ListNode[Animal](h: Animal, t: Node[Animal]) extends Node[Animal] { def prepend(elem: Animal) = ListNode[Animal](elem, this) def head: Animal = h def tail = t }
若是generic的類型是Cat的話,ListNode就變成以下
case class ListNode[Cat](h:Cat, t: Node[Cat]) extends Node[Cat] {
def prepend(elem:Cat) = ListNode[Cat](elem, this)
def head: Cat= h
def tail = t
}
addDogToAnimal方法接受一個ListNode[Animal],由於ListNode[Cat] 是 ListNode[Animal]的子類(由於是Covaraiance的)scala
因此咱們能夠addDogToAnimal(ListNode(Cat(), Nil())),可是ListNode[Cat]只能prepend是Cat類型的對象。因此必定會出問題。code
解決方法就是在全部須要消費者方法中 introducing a new type parameter U
that has B
as a lower type bound.htm
若是generic的類型是Animal的話,ListNode就變成以下trait Node[+B] { def prepend[U >: B](elem: U) } case class ListNode[+B](h: B, t: Node[B]) extends Node[B] { def prepend[U >: B](elem: U) = ListNode[U](elem, this) def head: B = h def tail = t } case class Nil[+B]() extends Node[B] { def prepend[U >: B](elem: U) = ListNode[U](elem, this) }
如今再來看剛纔的問題
case class ListNode[Animal](h: Animal, t: Node[Animal]) extends Node[Animal] { def prepend[U >: Animal](elem: Animal) = ListNode[Animal](elem, this) def head: Animal = h def tail = t }
若是generic的類型是Cat的話,ListNode就變成以下
case class ListNode[Cat](h:Cat, t: Node[Cat]) extends Node[Cat] { def prepend[U >: Cat](elem:Cat) = ListNode[Cat](elem, this) def head: Cat= h def tail = t }
ListNode[Cat]的prepend方法能夠接受全部U >: Cat 的對象
因此prepend方法能夠接受Animal的對象做爲參數。Dog也是一種Animal,因此animalNode.prepend(Dog())是沒有問題的在這裏以一個java開發者來講,會以爲很不合理。明明是cat類型的ListNode,怎麼能夠加入dog。但這也是scala和java的不一樣呀。唉ListNode[Cat] 仍是 ListNode[Animal]的子類
addDogToAnimal(ListNode(Cat(), Nil()))的時候,