No hablaría de 'sobrescribir' con respecto a los tipos, sino más bien estrechando sus límites.
type T
... no tiene límites
type T <: C
... T
es C
o un subtipo de C
(que se llama cota superior )
type T >: C
... T
es C
o un supertipo C
(que se llama límite inferior)
type T = C
... T
es exactamente C
(tipo alias)
Por lo tanto, si T
es un miembro de tipo de rasgo A
, y SubA
es un subtipo de A
, en el caso (2) SubA
puede estrechar T
a una más particular, subtipo C
, mientras que en el caso (3) podría reducirlo a un supertipo superior de C
. El caso (1) no impone ninguna restricción para SubA
, mientras que el caso (4) significa que T
es 'final' por así decirlo.
Esto tiene consecuencias para la capacidad de utilización de T
en A
-ya puede aparecer como el tipo de un argumento de un método o tipo de retorno de un método.
Ejemplo:
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
}
Es posible definir método m
en MayNarrow1
porque el tipo T
se produce en posición contravariant (como el tipo de un argumento del método), por lo que todavía es válida incluso si T
se estrecha hacia abajo en un subtipo de MayNarrow1
(el cuerpo del método puede tratar t
como si fuera del tipo C
).
En contraste, type T = C
arregla inevitablemente T
, que correspondería a hacer un método final
.Al fijar T
, que puede ser utilizado en una posición covariante (como tipo de retorno de un método):
trait Fixed extends MayNarrow1 {
type T = C // make that T <: C to see that it won't compile
final def test: T = new C {}
}
Ahora puede ver fácilmente que debe ser prohibido más 'anulación' T
:
trait Impossible extends Fixed {
override type T = SubC
test.bar // oops...
}
Para ser completa, aquí es el caso menos común de una cota inferior:
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 {}
}
Nota: la diferencia sutil entre los miembros de tipo abstracto con los límites frente a los parámetros de tipo con anotaciones de varianza ver post de Geoffrey Washburn en http://scala-programming-language.1934581.n4.nabble.com/Scala-type-override-td1943353 .html –