你不会说关于类型的“覆盖”,而是缩小它们的界限。
type T
...没有界限
type T <: C
...T
是C
或子类型C
(称为上限)
type T >: C
...T
是C
或超类型C
(称为下界)
type T = C
...T
正是C
(类型别名)
因此,如果T
是 trait 的类型成员A
,并且SubA
是 的子类型A
,在情况 (2)SubA
中可能会缩小T
到更特定的子类型C
,而在情况 (3) 中可能会缩小到更高的超类型C
。案例(1)没有对 施加任何限制SubA
,而案例(4)可以说T
是“最终的”。
这会对T
in的可用性产生影响——A
无论它可能显示为方法参数的类型还是方法的返回类型。
例子:
trait C { def foo = () }
trait SubC extends C { def bar = () }
trait MayNarrow1 {
type T <: C // allows contravariant positions in MayNarrow1
def m(t: T): Unit = t.foo // ...like this
}
object Narrowed1 extends MayNarrow1 {
type T = SubC
}
object Narrowed2 extends MayNarrow1 {
type T = SubC
override def m(t: T): Unit = t.bar
}
可以定义方法m
inMayNarrow1
因为 typeT
出现在逆变位置(作为方法参数的类型),因此即使T
在子类型中缩小它仍然有效MayNarrow1
(方法体可以将t
其视为 type C
)。
相反,type T = C
不可避免地修复T
,这有点对应于制作方法final
。通过修复T
,它可以用于协变位置(作为方法的返回类型):
trait Fixed extends MayNarrow1 {
type T = C // make that T <: C to see that it won't compile
final def test: T = new C {}
}
您现在可以很容易地看到必须禁止进一步“覆盖” T
:
trait Impossible extends Fixed {
override type T = SubC
test.bar // oops...
}
为了完整起见,下面是不太常见的下限情况:
trait MayNarrow2 {
type T >: SubC // allows covariant positions in MayNarrow2
def test: T = new SubC {}
}
object Narrowed3 extends MayNarrow2 {
type T = C
test.foo
}
object Narrowed4 extends MayNarrow2 {
type T = C
override def test: T = new C {}
}