Why is upcasting necessary in this Scala code?

2019-04-05 03:58发布

问题:

This compiles:

import scala.collection._

trait Foo[A, +This <: SortedSet[A] with SortedSetLike[A,This]]
extends SortedSetLike[A, This] { this: This =>

  def bar: This = (this: SortedSetLike[A,This]).empty

}

But if the upcast is removed it fails to compile:

import scala.collection._

trait Foo[A, +This <: SortedSet[A] with SortedSetLike[A,This]]
extends SortedSetLike[A, This] { this: This =>

  def bar: This = this.empty

}

Why? From the extends clause we know that Foo is a SortedSetLike[A, This], so the upcast is valid of course - but doesn't this show that the compiler has allowed conflicting inheritance to occur?

回答1:

The SortedSetLike trait inherits the empty method from SetLike.

/** The empty set of the same type as this set
* @return  an empty set of type `This`.
*/
def empty: This

But SortedSet overrides the empty method and has an explicit return type:

/** Needs to be overridden in subclasses. */
override def empty: SortedSet[A] = SortedSet.empty[A]

Since you specify that This is a subclass of SortedSet the compiler will find SortedSet's implementation of empty first, which returns a SortedSet. The compiler does not know how to convert the resulting SortedSet to your This subclass.

But if you upcast to the SortedSetLike trait the compiler will find its empty method which returns a This.