Non si parlerà di "sovrascrittura" per quanto riguarda i tipi, ma piuttosto restringimento dei limiti.
type T
... limiti
type T <: C
... T
è C
o un sottotipo di C
(che è chiamato limite superiore)
type T >: C
... T
è C
o un supertipo di C
(che è chiamato limite inferiore)
type T = C
... T
è esattamente C
(tipo alias)
Pertanto, se T
è un membro tipo di tratto A
, e SubA
è un sottotipo di A
, nel caso (2) SubA
possono restringere T
ad una più particolare sottotipo C
, mentre nel caso (3) si potrebbe restringere a un supertipo superiore di C
. Il caso (1) non impone alcuna restrizione per SubA
, mentre il caso (4) significa che T
è "finale" per così dire.
Ciò incide l'utilizzabilità di T
in A
-se può apparire come tipo metodo dell'argomento o tipo di ritorno del metodo.
Esempio:
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
}
È possibile definire metodo m
in MayNarrow1
perché tipo T
verifica in posizione contravariant (come tipo di metodo di argomento), quindi è ancora valida anche se T
si restringe verso il basso in un sottotipo di MayNarrow1
(il corpo del metodo può trattare t
come se fosse il tipo C
).
Al contrario, type T = C
corregge inevitabilmente T
, che corrisponderebbe a un metodo final
.Fissando T
, può essere utilizzato in una posizione covariante (come tipo di ritorno di un metodo):
trait Fixed extends MayNarrow1 {
type T = C // make that T <: C to see that it won't compile
final def test: T = new C {}
}
Si può ora facilmente vedere che deve essere vietato ulteriormente 'esclusione' T
:
trait Impossible extends Fixed {
override type T = SubC
test.bar // oops...
}
Per essere completa, qui è il caso meno comune di un limite inferiore:
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: per la sottile differenza tra membri di tipo astratto con limiti rispetto ai parametri di tipo con annotazioni di varianza, vedere il post di Geoffrey Washburn in http://scala-programming-language.1934581.n4.nabble.com/Scala-type-override-td1943353 .html –