当T需要处于协变和逆变位置时的方差

时间:2017-03-03 17:40:26

标签: scala

这是我的问题,我有一个像这样定义的类型类:

trait Alterer[T] {
  def alter(t: T): T
}

object Alterer {
  implicit val stringPrinter = new Alterer[String]{
    override def alter(s: String) =  "hi " + s
  }
}

object Alter {
  def alter[T](obj: T)(implicit alterer: Alterer[T]) = alterer.alter(obj)
}

val bob2 = Alter.alter("bob") // hi bob

现在我想为其他类型定义隐式实例。 但我希望能够定义一个超类型。 Any作为超类型的示例:

case class Person(name: String, age: Int)

implicit val anyPrinter = new Alterer[Any]{
  override def alter(a: Any) = ("hi any " + a.toString)
}

Alter.alter(Person("joe", 34))

这不会编译,因为如果我想让Alterer [Any]被视为Alterer [Person],我的T类型必须是逆变的。

所以我会这样做:

trait Alterer[-T] {
   def alter(t: T): T
}

如果alter方法也没有返回T,那就没关系。 这里编译器抱怨因为T返回类型不在逆变位置。

有没有办法解决这个问题?

由于

1 个答案:

答案 0 :(得分:1)

您需要两个类型参数,一个用于逆变输入,另一个用于协变输出。

trait Alterer[-T, +U] {
   def alter(t: T): U
}

然后你可以宣布一个

  implicit val stringPrinter = new Alterer[String, String]{
    override def alter(s: String) =  "hi " + s
  }

对于字符串或

implicit val anyPrinter = new Alterer[Any, String]{
  override def alter(a: Any) = ("hi any " + a.toString)
}

将Any转换为字符串并预先发布“hi any”

如果你想强制输入和输出之间的子类型关系,你可以使用一个抽象类来代替你的特性,该抽象类在其构造函数中接受一个子类型证据。

abstract class Alterer[-T, +U](implicit ev: U <:< T) {
  def alter(t: T): U
}

<:<在predef中定义,并强制U是T的子类型。

此更改后,其余代码保持不变。