上限类型允许子类型而不是父类型

时间:2018-05-03 14:20:22

标签: scala generics polymorphism type-bounds

是否可以使用一个类型绑定的泛型方法,该类型绑定等于此特征的每个可能的具体子类,而不是特征本身?"

例如,假设我有以下继承层次结构:

sealed trait Fruit

case class Apple() extends Fruit
case class Orange() extends Fruit
...
case class Watermelon() extends Fruit

我想定义一个方法def eatFruit[T <: ???](fruit: Seq[T]),它允许T属于AppleOrangeWatermelon等类型但不属于类型Fruit。绑定类型[T <: Fruit]显然无法完成工作。

最初的推动力是我们有一个FruitRepository类,允许不同水果的批量/批量插入。批处理是在类外部完成的,所以目前它有很多方法,包括很多重复的逻辑,包括创建批处理的saveApples(apples: Seq[Apple])saveOranges(oranges: Seq[Orange])等等。更新声明。我想以更通用的方式管理此问题,但任何方法saveFruit(fruit: Seq[Fruit])都可以允许包含苹果和橙子的列表,存储库无法处理。

...我也承认,我现在一般都很好奇这种类型绑定是否可行,即使我们最终以不同的方式解决了存储库问题。

1 个答案:

答案 0 :(得分:2)

我们可以将上限指令与类型不等式的自定义隐式执行相结合。 Taken from here(或通常见Enforce type difference):

@annotation.implicitNotFound(msg = "Cannot prove that ${A} =!= ${B}.")
trait =!=[A, B]
object =!= {
  class Impl[A, B]
  object Impl {
    implicit def neq[A, B] : A Impl B = null
    implicit def neqAmbig1[A] : A Impl A = null
    implicit def neqAmbig2[A] : A Impl A = null
  }

  implicit def foo[A, B](implicit e: A Impl B): A =!= B = null
}

然后我们这样做:

def eatFruit[T <: Fruit](implicit ev: T =!= Fruit) = ???

当我们称之为:

def main(args: Array[String]): Unit = {
  eatFruit[Fruit]
}

我们得到:

Error:(29, 13) Cannot prove that yuval.tests.FooBar.Fruit =!= yuval.tests.FooBar.Fruit.
    eatFruit[Fruit]

但这会编译:

eatFruit[Orange]

这里所有的魔力都是由于在[A, A]对的范围内造成了隐含的模糊性,因此编译器会抱怨。

我们还可以更进一步,并实现我们自己的逻辑类型,例如,让我们称之为=<:=!=。我们可以稍微改变以前的实现:

@annotation.implicitNotFound(msg = "Cannot prove that ${A} =<:=!= ${B}.")
trait =<:=!=[A,B]
object =<:=!= {
  class Impl[A, B]
  object Impl {
    implicit def subtypeneq[B, A <: B] : A Impl B = null
    implicit def subneqAmbig1[A] : A Impl A = null
    implicit def subneqAmbig2[A] : A Impl A = null
  }

  implicit def foo[A, B](implicit e: A Impl B): A =<:=!= B = null
}

现在:

case class Blue()

def main(args: Array[String]): Unit = {
  eatFruit[Fruit] // Doesn't compile
  eatFruit[Blue] // Doesn't compile
  eatFruit[Orange] // Compiles
}