Scala - 具有下限的通用隐式

Scala - Generic implicit with lower bound

考虑以下几点:

class Super
class Sub extends Super

implicit val implicitOption: Option[Super] = None
def f[A, B >: A](a: A)(implicit i: Option[B]) = println("It worked")

如果我调用 f(new Super),它工作正常,但是 f(new Sub) 给我一个编译错误(找不到参数 i 的隐式值)。

为什么implicitOptionA = Sub时不能作为隐式参数?

在这种情况下,编译器需要一些帮助来确定要使用哪个 B。所以

f[Sub, Super](new Sub)

工作正常。

--- 旧答案 - 仍然相关,但从不同的角度出发 --

因为 Option 是协变的 - 这意味着您可以在需要 Option[Super] 的地方传递 Option[Sub],但反之则不行。

要完成这项工作,您需要一个逆变 class(我们暂时称它为 ContraOption)- 这样您就可以将 ContraOption[Super] 传递给 ContraOption[Sub]预计。

继续你的例子:

class Super
class Sub extends Super

implicit val implicitOption: Option[Super] = None

sealed abstract class ContraOption[-A]
case object ContraNone extends ContraOption[Any]
case class ContraSome[A](value: A) extends ContraOption[A]

implicit val implicitContraOption: ContraOption[Super] = ContraNone

def f[A, B >: A](a: A)(implicit i: Option[B]) = println("It worked")
def f2[A, B >: A](a: A)(implicit i: ContraOption[B]) = println("It worked 2")

f(new Super)  // It worked
f2(new Sub)   // It worked 2

请注意,尝试执行 f(new Sub)(如您的示例)和 f2(new Super) 都无法通过 "implicit not found" 进行编译。

关于协变和逆变的更基本的理解,请参考docs。但简单地说,协变泛型 class "follows" 祖先-后代链接,而逆变 classes 反向。为了说明(在伪scala中):

class Super
class Sub extends Super

class Covariant[+A]
class Contravariant[-A]

This gives us the following relationships:
Sub <: Super
Covariant[Sub] <: Covariant[Super]
Contravariant[Super] <: Covariant[Sub]

解决方案原来是将参数 Bf 移动到 implicitOption,方法是像这样更改它们的定义。

implicit def implicitOption[B <: Super]: Option[B] = None
def f[A](a: A)(implicit i: Option[A]) = println("It worked")