Scala Covariance and Lower Type Bounds Explanation

前端 未结 4 1146
说谎
说谎 2021-02-02 00:41

I am trying to get my head around covariance in respect with methods creating new immutable types using lower bounds

class ImmutableArray[+T](item: T, existing:          


        
4条回答
  •  清酒与你
    2021-02-02 01:00

    Consider the followng hierarchy:

    class Foo
    class Bar extends Foo { def bar = () }
    class Baz extends Bar { def baz = () }
    

    And a class similar to yours:

    class Cov[+T](val item: T, val existing: List[T] = Nil) {
      def append[S >: T](value: S) = new Cov[S](value, item :: existing)
    }
    

    Then we can construct three instances for each of the Foo sub-types:

    val cFoo = new Cov(new Foo)
    val cBar = new Cov(new Bar)
    val cBaz = new Cov(new Baz)
    

    And a test function that requires bar elements:

    def test(c: Cov[Bar]) = c.item.bar
    

    It holds:

    test(cFoo) // not possible (otherwise `bar` would produce a problem)
    test(cBaz) // ok, since T covariant, Baz <: Bar --> Cov[Baz] <: Cov[Bar]; Baz has bar
    

    Now the append method, falling back to upper bound:

    val cFoo2 = cBar.append(new Foo)
    

    This is ok, because Foo >: Bar, List[Foo] >: List[Bar], Cov[Foo] >: Cov[Bar].

    Now, correctly your bar access has gone:

    cFoo2.item.bar // bar is not a member of Foo
    

    To understand why you need the upper-bound, imagine the following was possible

    class Cov[+T](val item: T, val existing: List[T] = Nil) {
      def append(value: T) = new Cov[T](value, item :: existing)
    }
    
    class BarCov extends Cov[Bar](new Bar) {
      override def append(value: Bar) = {
        value.bar // !
        super.append(value)
      }
    }
    

    Then you could write

    def test2[T](cov: Cov[T], elem: T): Cov[T] = cov.append(elem)
    

    And the following illegal behaviour would be allowed:

    test2[Foo](new BarCov, new Foo) // BarCov <: Cov[Foo]
    

    where value.bar would be called on a Foo. Using (correctly) the upper bound, you wouldn't be able to implement append as in the hypothetical last example:

    class BarCov extends Cov[Bar](new Bar) {
      override def append[S >: Bar](value: S) = {
        value.bar // error: value bar is not a member of type parameter S
        super.append(value)
      }
    }
    

    So the type system remains sound.

提交回复
热议问题