如果传递第一个操作数,则不会出现该问题a: A
到有机会推断外部可见类型的方法/类构造函数A
:
trait Animal
trait Eats[A <: Animal, B <: Animal]
object Eats {
def apply[A <: Animal, B <: Animal]: Eats[A, B] = new Eats[A, B] {}
}
implicit class EatsOps[A <: Animal](a: A) {
def eat[B <: Animal](food: B)(implicit e: Eats[A, B]) =
printf(s"%s eats %s\n", a, food)
}
case class Cat() extends Animal
case class Bird() extends Animal
case class Worm() extends Animal
implicit val e1 = Eats[Cat, Bird]
implicit val e2 = Eats[Bird, Worm]
val cat = Cat()
val bird = Bird()
val worm = Worm()
// c eat c // nope
cat eat bird
// c eat w // nope
// b eat c // nope
// b eat b // nope
bird eat worm
// w eat c // nope
// w eat b // nope
// w eat w // nope
Here, EatsOps[A <: Animal]
可以先推断出什么A
是,那么在eat[B <: Animal]
它可以推断出什么B
是,并使用有关两者的信息A
and B
插入正确的隐式。没有类型成员,扩展时无需执行任何操作Animal
.
这有点像 XY 问题的 X 解决方案。是的,我重复使用了Animal
代替Food
...
Update
如果你想访问某个特定的私有方法Animal
调用时执行eat
,通常的方法是将所有基本功能移至Eats
特征,然后提供实例Eats
在特定的伴随对象中Animal
。例如,我们可以这样让Cat
做它不可思议的事private
在真正吃之前的东西Bird
:
trait Eats[A <: Animal, B <: Animal] {
def apply(a: A, food: B): Unit
}
object Eats {
def apply[A <: Animal, B <: Animal]: Eats[A, B] = new Eats[A, B] {
def apply(animal: A, food: B) = println(s"${animal} eats ${food}")
}
}
implicit class EatsOps[A <: Animal](animal: A) {
def eat[B <: Animal](food: B)(implicit e: Eats[A, B]) = e(animal, food)
}
case class Cat() extends Animal {
private def privateCatMethod(b: Bird): Unit = {}
}
object Cat {
implicit val catsEatBirds: Eats[Cat, Bird] = new Eats[Cat, Bird] {
def apply(c: Cat, b: Bird): Unit = {
c.privateCatMethod(b)
println(s"{c} eats {b}")
}
}
}
其余代码将保持不变,只是不需要e1: Eats[Cat, Bird]
不再有。