scala> class C
defined class C
scala> class subC extends C
defined class subC
scala> class A { type T = C}
defined class A
scala> class subA extends A { override type T = subC}
<console>:10: error: overriding type T in class A, which equals C;
type T has incompatible type
class subA extends A { override type T = subC}
^
在上面的示例中,我收到一条错误消息,我无法覆盖类A
中的类型字段(即使所选类型subC
扩展了类C
)。
是否可以覆盖类型字段?如果是的话,上面的例子有什么问题?
答案 0 :(得分:25)
你不会谈论关于类型的'覆盖',而是缩小它们的界限。
type T
...无界限type T <: C
... T
是C
或C
的子类型(称为上限)type T >: C
... T
是C
或超级C
(称为下限)type T = C
... T
正好是C
(类型别名)因此,如果T
是特质A
的类型成员,而SubA
是A
的子类型,则情况(2)SubA
可能将T
缩小到更具体的子类型C
,而在情况(3)中,它可以将其缩小到更高的C
超类型。案例(1)不对SubA
施加任何限制,而案例(4)意味着T
可以说是'最终'。
这会对T
中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
中定义方法MayNarrow1
,因为类型T
出现在逆变位置(作为方法参数的类型),因此它仍然是即使在T
的子类型中缩小MayNarrow1
也是有效的(方法正文可以将t
视为类型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 {}
}