Ответ 1
Вы бы не говорили о "переопределении" по отношению к типам, а скорее о сужении своих границ.
-
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
в 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
}
В MayNarrow1
можно определить метод m
, потому что тип 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 {}
}