我有一个Animal特性和一些案例类,如下所示
sealed trait Animal
trait Domestic extends Animal
trait Wild extends Animal
case class Dog(id: UUID = UUID.randomUUID()) extends Domestic
case class Lion(id: UUID = UUID.randomUUID()) extends Wild这里是我的羊群类,它可以包含一种类型的动物的列表。
case class Herd[T <: Animal](get: T*)我想创造一群单一类型的动物。
val herd1 = Herd(Cat(), Cat())
val herd2 = Herd(Cat(), Lion())在Scala中两者都是有效的,但是如果您看一群猫和狮子的含义,这是没有意义的。是否有办法将Herd限制为单一类型?
发布于 2021-05-31 13:11:56
尝试引入两个类型参数A和B,然后将它们与一个通用类型约束A =:= B关联起来。
case class Herd[A <: Animal, B](x: A, y: B*)(implicit ev: A =:= B)
Herd(Lion()) // ok
Herd(Lion(), Lion()) // ok
Herd(Cat(), Lion()) // compile-time error=:=到底是什么
考虑具有两个类型参数A和B的以下方法,我们的目的是表示它们应该相等,或者至少A应该是B的一个子类型。
scala> def f[A, B](a: A, b: B): B = {
| val x: B = a
| x
| }
val x: B = a
^
On line 2: error: type mismatch;
found : a.type (with underlying type A)
required: B在上述定义中,这两种类型参数是完全不相关的,且方法主体不影响类型参数的类型推断,因而产生了误差。现在,让我们尝试将它们与类型绑定的A <: B关联起来
scala> def f[A <: B, B](a: A, b: B): B = {
| val x: B = a
| x
| }
def f[A <: B, B](a: A, b: B): B因此,这将编译,但是编译器总是试图通过计算给定参数的最小上限来满足类型界限。
scala> f(Lion(), Dog())
val res32: Product with Animal with java.io.Serializable = Lion(...)我们需要更多的东西来绕过编译器推导出最小上限的倾向,这就是泛化类型相等约束发挥作用的地方。
scala> def f[A <: B, B](a: A, b: B)(implicit ev: A =:= B): B = {
| val x: B = a
| x
| }
def f[A <: B, B](a: A, b: B)(implicit ev: A =:= B): B
scala> f(Lion(), Cat())
^
error: Cannot prove that Lion =:= Product with Animal with java.io.Serializable.现在编译器仍然必须尝试生成给定参数的最小上限,但是它还必须满足能够生成两种类型的ev ( A和B相等)的附加要求。(注意,如果可能,见证ev将由编译器自动实例化。)
一旦我们有了见证ev,我们就可以通过它的apply方法在A和B类型之间自由移动,例如,考虑
scala> type A = Lion
type A
scala> type B = Lion
type B
scala> val a: A = Lion()
val a: A = Lion(...)
scala> val ev: =:=[A, B] = implicitly[A =:= B]
val ev: A =:= B = generalized constraint
scala> ev.apply(a)
val res44: B = Lion(...)注意ev.apply(a)如何类型为B。我们可以这样应用=:=的原因是因为它实际上是一个函数。
scala> implicitly[(A =:= B) <:< Function1[A, B]]
val res43: A =:= B <:< A => B = generalized constraint所以隐式参数列表
(implicit ev: A =:= B)实际上是指定隐式转换函数。
(implicit ev: A => B)因此,现在编译器能够在需要的地方自动注入隐式转换,如下所示
def f[A <: B, B](a: A, b: B)(implicit ev: A =:= B): B = {
val x: B = a
x
}自动展开为
def f[A <: B, B](a: A, b: B)(implicit ev: A =:= B): B = {
val x: B = ev.apply(a)
x
}总之,与类型界限一样,泛化的类型约束也是要求编译器在编译时对代码基进行进一步检查的另一种方式。
https://stackoverflow.com/questions/67773938
复制相似问题