Scala類型系統(sudden thought)

  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()))的時候
相關文章
相關標籤/搜索