我很困惑为什么scala在计算方差约束时会对def
和type
有不同的看法?
我试图打破我的文本墙,并将我的返回类型分解为一个单独的类型声明,并立即得到一个方差错误。这是一个最小化的例子:
class Bad[+A] {
type Check[B >: A] = Bad[B]
def check[B >: A] : Check[B] = this
}
失败了:
covariant.scala:2: error: covariant type A occurs in invariant position in type >: A of type B
type Check[B >: A] = Bad[B]
^
one error found
不过,它没有额外的类型声明它工作正常:
class Good[+A] {
def check[B >: A]: Good[B] = this
}
class Ugly[+A] {
type Check[B >: A @scala.annotation.unchecked.uncheckedVariance] = Ugly[B]
def check[B >: A] : Check[B] = this
}
这是因为只要类型成员Check
逃逸到外部,它就会立即出现在函数和方法的共同和逆变位置:
class Bad[+A] {
type Check[B]
}
class B
class A extends B
val bad: Bad[A] = ???
import bad._
def hypotheticalMethodSomewhereOutside1(c: Check[B]) = ???
def hypotheticalMethodSomewhereOutside2(i: Int): Check[B] = ???
[B >: A]
方法中check
类型参数的关键区别
def check[B >: A]: Good[B] = this
是单一方法check
在你的控制之下,你可以保证它不会在非协变位置使用A
。
与此相反,类型成员Check
可以出现在无变量和逆变位置的无限多种其他方法中,并且这些方法不在您的控制之下,因此您不能禁止在Check
看起来不协变的位置使用A
,例如你不能阻止上面例子中的hypotheticalMethodSomewhereOutsideN
被其他人定义。
请注意,check
示例失败时,Bad
方法的存在不是必需的:
// error: covariant type A occurs in invariant position in type >: A of type B
class Bad[+A] {
type Check[B >: A] = Bad[B]
// def check[B >: A] : Check[B] = this
}
但是,如果你隐藏每个人的成员类型Check
(真的是每个人,包括同一个类的其他实例,也就是说,使用极其隐蔽的private[this]
访问修饰符):
class Bad[+A] {
private[this] type Check[B >: A] = Bad[B]
}
它汇编得很好。
private[this]
解决了这个问题,因为使用这个修饰符,只需要检查类中的方法,不必考虑外面某处的假设方法:
class Ok[+A] {
private[this] type Check[B >: A] = Ok[B]
def ok[B >: A](c: Check[B]): Unit = ???
def alsoOk[B >: A]: Check[B] = ???
}
注意,上面的内容可以写成
class Good[+A] {
type Check[+B] = Good[B]
def ok[B >: A](c: Check[B]): Unit = ???
def alsoOk[B >: A]: Check[B] = ???
}
只有当类型构造函数Good
具有比Check
更多的参数时才有意义。