В этом случае компилятору нужна помощь, чтобы выяснить, какой B
использовать. Так что
f[Sub, Super](new Sub)
работает просто отлично.
--- Старый ответ - все еще актуален, но подходит с другой точки зрения -
Поскольку Option
является ковариантным - что означает вы можете передать Option[Sub]
там, где ожидается Option[Super]
, но не наоборот.
Чтобы это сработало, вам понадобится контравариантный класс (на данный момент назовем его 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)
не компилируется с "неявным не найдено".
Для более фундаментального понимания ко-и контравариантности, пожалуйста, обратитесь к документам . Проще говоря, ковариантный класс generi c «следует» за связями предков и потомков, в то время как контравариантные классы меняют направление. Для иллюстрации (в псевдо- 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]