使用带有子类型参数的超级方法
Use super method with sub type param
我正在尝试在超级 class 中实现一些功能,这样我就不必总是在其子项中重复它。示例:
trait Animal {
def applyF(transition: Animal => Animal): Animal = transition(this) // Animal as param and return type
}
case class Cat(color: String) extends Animal {
def changeColor(color: String): Cat = this.copy(color)
def update(): Animal = {
val transition = (cat: Cat) => cat.changeColor("yellow") // Cat as param and return type
applyF(transition) // <-- Type mismatch, expected: Animal => Animal, actual: Cat => Cat
}
}
但这会导致类型不匹配,因为 Cat
不是 Animal
。为什么这不起作用?
Cat 扩展了 Animal,所以它应该是动物吧?
这与co-/contravariant有关吗?
我该如何解决?
-----更新-----
第二个例子:
trait Animal {
def applyF[A >: this.type <: Animal](transitions: Iterable[A => Animal]): Animal =
transitions.foldLeft(this)((animal, transition) => transition(animal))
}
case class Cat(color: String) extends Animal {
def changeColor(color: String): Cat = this.copy(color)
def update(): Animal = {
val transition = (cat: Cat) => cat.changeColor("yellow") // Cat as param and return type
applyF(Iterable(transition)) // <-- Type mismatch, expected: A, actual: entity.type (with underlying type example.state.Entity)
}
}
Cat
扩展了 Animal
但 Cat => Cat
没有扩展 Animal => Animal
.
A => B
对于 B
是协变的,对于 A
是逆变的,即如果 A1 <: A
,B1 <: B
那么 A => B1 <: A => B <: A1 => B
.
如果参数化 Animal#applyF
会怎样?
trait Animal {
def applyF[A >: this.type <: Animal](transition: A => Animal): Animal = transition(this)
}
trait Animal {
def applyF[A >: this.type <: Animal](transitions: Iterable[A => A]): Animal /*A*/ =
transitions.foldLeft[A](this)((animal, transition) => transition(animal))
}
其他选项是使用 F-Bounded Polymorphism。
trait Animal[A <: Animal[A]] { self: A =>
def applyF(transition: Iterable[A => A]): A = // I would use List instead of Iterable.
transition.foldLeft(this)((animal, transition) => transition(animal))
}
final case class Cat(color: String) extends Animal[Cat] {
def changeColor(color: String): Cat = this.copy(color)
def update(): Cat =
applyF(List(cat => cat.changeColor("yellow")))
}
但是,请记住 it does bring its own problems。
我正在尝试在超级 class 中实现一些功能,这样我就不必总是在其子项中重复它。示例:
trait Animal {
def applyF(transition: Animal => Animal): Animal = transition(this) // Animal as param and return type
}
case class Cat(color: String) extends Animal {
def changeColor(color: String): Cat = this.copy(color)
def update(): Animal = {
val transition = (cat: Cat) => cat.changeColor("yellow") // Cat as param and return type
applyF(transition) // <-- Type mismatch, expected: Animal => Animal, actual: Cat => Cat
}
}
但这会导致类型不匹配,因为 Cat
不是 Animal
。为什么这不起作用?
Cat 扩展了 Animal,所以它应该是动物吧?
这与co-/contravariant有关吗?
我该如何解决?
-----更新-----
第二个例子:
trait Animal {
def applyF[A >: this.type <: Animal](transitions: Iterable[A => Animal]): Animal =
transitions.foldLeft(this)((animal, transition) => transition(animal))
}
case class Cat(color: String) extends Animal {
def changeColor(color: String): Cat = this.copy(color)
def update(): Animal = {
val transition = (cat: Cat) => cat.changeColor("yellow") // Cat as param and return type
applyF(Iterable(transition)) // <-- Type mismatch, expected: A, actual: entity.type (with underlying type example.state.Entity)
}
}
Cat
扩展了 Animal
但 Cat => Cat
没有扩展 Animal => Animal
.
A => B
对于 B
是协变的,对于 A
是逆变的,即如果 A1 <: A
,B1 <: B
那么 A => B1 <: A => B <: A1 => B
.
如果参数化 Animal#applyF
会怎样?
trait Animal {
def applyF[A >: this.type <: Animal](transition: A => Animal): Animal = transition(this)
}
trait Animal {
def applyF[A >: this.type <: Animal](transitions: Iterable[A => A]): Animal /*A*/ =
transitions.foldLeft[A](this)((animal, transition) => transition(animal))
}
其他选项是使用 F-Bounded Polymorphism。
trait Animal[A <: Animal[A]] { self: A =>
def applyF(transition: Iterable[A => A]): A = // I would use List instead of Iterable.
transition.foldLeft(this)((animal, transition) => transition(animal))
}
final case class Cat(color: String) extends Animal[Cat] {
def changeColor(color: String): Cat = this.copy(color)
def update(): Cat =
applyF(List(cat => cat.changeColor("yellow")))
}
但是,请记住 it does bring its own problems。