Why doesn't `out` allow a generic Kotlin interface to be specialized in child classes?

67 Views Asked by At

I have the following two classes in a new body of Kotlin code:

open class Parent {
  open fun f(a : Double) : Parent {
    TODO("Implement")
  }
}

class Child : Parent() {
  override fun f(a : Double) : Child {
    TODO("Implement")
  }
}

The child class is legally overriding the parent's implementation of f in this case. The argument type is the same, and the return type is allowed to be child of the return type given in the parent. So far so good.

I also have an existing code base that has an interface and many implementations of that interface that have this pattern:

interface F<out T> {
  fun f(a : Double) : T
}

class Uncle : F<Uncle> {
  override fun f(a : Double) : Uncle {
    TODO("Implement")
  }
}

I would like to make the new code work with the existing code base by having Parent and Child implement the interface F, but I'm stuck. If I make Parent : F<Parent> that is fine for the parent, but then I'm not allowed to have Child : Parent(), F<Child> because Child now has inconsistent values for the parameter to the interface F according to the compiler. If I make Parent : F<Parent> and don't specify an interface explicitly for Child : Parent(), then the child class doesn't quite have the same behavior as expected in some cases because this doesn't capture the "sameness" of the return type on f for instances of the child class.

For a general pair of classes and an interface without out on the parameter, this problem is expected and ultimately related to type erasure. (See Any way to inherit from same generic interface twice (with separate types) in Kotlin? and Kotlin class that implements generic interface twice (with different types) .)

My situation is more specific though. If it were allowed, I would have F<Child> is effectively a subclass of F<Parent> due to the use of out. (See What is out keyword in kotlin .) I don't think the type-erasure argument holds for this case.

So two related questions:

  1. Is there an underlying reason that this cannot be supported (like the type-erasure argument for the general case)?
  2. Is there a good work-around?
0

There are 0 best solutions below