类型上限允许子类型但不允许父类型
Upper type bound allowing subtypes but not the parent type
是否可以使用类型绑定的泛型方法达到 "every possible concrete subclass of this trait, but not the trait itself?"
例如,假设我有以下继承层次结构:
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
为 Apple
、Orange
、Watermelon
等类型,但不允许类型 Fruit
。类型绑定 [T <: Fruit]
显然不能完成这项工作。
这样做的最初动力是我们有一个 FruitRepository
class 允许 batched/bulk 插入不同的水果。批处理是在 class 外部完成的,因此目前它有很多方法,如 saveApples(apples: Seq[Apple])
、saveOranges(oranges: Seq[Orange])
等,其中包含大量涉及创建批量更新语句。我想以更通用的方式来管理它,但是任何方法 saveFruit(fruit: Seq[Fruit])
都允许例如包含存储库无法处理的苹果和橙子的列表。
...我也承认我现在通常很好奇这种类型绑定是否可能,即使我们最终以不同的方式解决存储库问题。
我们可以将上限指令与类型不等式的自定义隐式强制执行相结合。 Taken from here (or generally see: 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
}
是否可以使用类型绑定的泛型方法达到 "every possible concrete subclass of this trait, but not the trait itself?"
例如,假设我有以下继承层次结构:
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
为 Apple
、Orange
、Watermelon
等类型,但不允许类型 Fruit
。类型绑定 [T <: Fruit]
显然不能完成这项工作。
这样做的最初动力是我们有一个 FruitRepository
class 允许 batched/bulk 插入不同的水果。批处理是在 class 外部完成的,因此目前它有很多方法,如 saveApples(apples: Seq[Apple])
、saveOranges(oranges: Seq[Orange])
等,其中包含大量涉及创建批量更新语句。我想以更通用的方式来管理它,但是任何方法 saveFruit(fruit: Seq[Fruit])
都允许例如包含存储库无法处理的苹果和橙子的列表。
...我也承认我现在通常很好奇这种类型绑定是否可能,即使我们最终以不同的方式解决存储库问题。
我们可以将上限指令与类型不等式的自定义隐式强制执行相结合。 Taken from here (or generally see: 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
}