Toby Hobson Toby Hobson - 2 months ago 10
Scala Question

Is this cast to subtype safe?

Given these classes and variables:

abstract class Base[T <: Base[_]] {
val self: T
def me(): T = self
}

class Derived extends Base[Derived] {
lazy val self = this
def whoAmI() = "Im derived"
}

val d = new Derived


I can safely call
d.foo().whoAmI()


But would this also be type safe?

abstract class Base[T <: Base[_]] {
def me(): T = this.asInstanceOf[T]
}

class Derived extends Base[Derived] {
def whoAmI() = "Im derived"
}


I'm thinking about edge cases in which other classes extend from Derived and the cast may blow up

m-z m-z
Answer

It isn't type safe. If you find you need to use asInstanceOf to make it compile, the answer will overwhelmingly be "No". The only time it is "safe" to cast to a sub-type is if there is only one sub-type. Otherwise, you cannot make any guarantees.

Consider this example:

abstract class Base[T <: Base[_]] {
  def me(): T = this.asInstanceOf[T]
}

class A extends Base[A]
class B extends Base[A]

scala> val b = new B
b: B = B@4b44655e

scala> b.me
java.lang.ClassCastException: B cannot be cast to A
  ... 33 elided

There is no restriction that the T in Base we're extending must be the same as the sub-type we are creating--only that they both extend Base. A and B are both a Base[_], but a B is not an A, so casting to A would be unsafe.

This is easy to fix by introducing a self-type within Base that requires it is also a T. Then, we can be sure that this is a T and don't need to cast.

abstract class Base[T <: Base[_]] { this: T =>
  def me(): T = this
}

This will no longer compile:

scala> class B extends Base[A]
<console>:12: error: illegal inheritance;
 self-type B does not conform to Base[A]'s selftype A
       class B extends Base[A]
                       ^