Is it possible to override a type field?

后端 未结 1 1218
旧时难觅i
旧时难觅i 2020-12-30 02:37
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         


        
1条回答
  •  情深已故
    2020-12-30 03:00

    You wouldn't speak of 'overriding' with respect to types, but rather narrowing their bounds.

    1. type T ... no bounds
    2. type T <: C ... T is C or a subtype of C (which is called upper bound)
    3. type T >: C ... T is C or a supertype of C (which is called lower bound)
    4. type T = C ... T is exactly C (type alias)

    Therefore, if T is a type member of trait A, and SubA is a subtype of A, in case (2) SubA may narrow T to a more particular subtype C, whereas in case (3) it could narrow it to a higher supertype of C. Case (1) doesn't impose any restrictions for SubA, while case (4) means that T is 'final' so to speak.


    This has consequences for the useability of T in A—whether it may appear as a method argument's type or a method's return type.

    Example:

    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
    }
    

    It is possible to define method m in MayNarrow1 because type T occurs in contravariant position (as a method argument's type), therefore it is still valid even if T is narrowed down in a subtype of MayNarrow1 (the method body can treat t as if it were type C).

    In contrast, type T = C inevitably fixes T, which would kind of correspond to making a method final. By fixing T, it can be used in a covariant position (as a method's return type):

    trait Fixed extends MayNarrow1 {
      type T = C   // make that T <: C to see that it won't compile
      final def test: T = new C {}
    }
    

    You can now easily see that it must be forbidden to further 'override' T:

    trait Impossible extends Fixed {
      override type T = SubC
    
      test.bar  // oops...
    }
    

    To be complete, here is the less common case of a lower bound:

    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 {}
    }
    

    0 讨论(0)
提交回复
热议问题