2012-06-30 14 views
14
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} 
             ^

En el ejemplo anterior, aparece un mensaje de error, que no puedo anular el campo de tipo en la clase A (incluso si el tipo elegido subC extiende la clase C).¿Es posible anular un campo de tipo?

¿Está anulando un campo de tipo posible? Y si es así, ¿qué pasa con el ejemplo anterior?

Respuesta

22

No hablaría de 'sobrescribir' con respecto a los tipos, sino más bien estrechando sus límites.

  1. type T ... no tiene límites
  2. type T <: C ... T es C o un subtipo de C (que se llama cota superior )
  3. type T >: C ... T es C o un supertipo C (que se llama límite inferior)
  4. 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 {} 
} 
+1

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 –

Cuestiones relacionadas