0
votes

Sometimes, a type is declared to implement 2 interfaces which both have an abstract property the same name, but when I moved the bounded type argument to a top-level abstract superclass and subclass it in different objects, the class overriding the the abstract property on both superinterfaces fails to compile when using type arguments for a subclass.

The generated interfaces and abstract class:

interface Actor : Type {
  val login: Stub<String>
  val posts: Config<String, BasePostsArgs>
}

interface SomeConflict : Type {
  val posts: Config<String, BasePostsArgs>
}

abstract class BasePostsArgs(
  args: ArgBuilder = ArgBuilder.create<String, BasePostsArgs>()) 
  : ArgBuilder by args

An example of the generated object which overrides 2 superinterface properties with the same name:

object Organization : Type, SomeConflict, Actor {

  override val login: Stub<String> = stub() 

  override val posts: Config<String, Organization.PostsArgs> = configStub(PostsArgs())

  class PostsArgs(args: ArgBuilder = ArgBuilder.create<String, PostsArgs>()) 
    : BasePostsArgs(args) {

    fun first(value: Int): PostsArgs = apply { addArg("size", value) }
    fun since(value: Date): PostsArgs = apply { addArg("since", value) }
  }
}

And then the interfaces for the API:

interface Type {
  fun <T> stub(): Stub<T> = StubImpl<T, ArgBuilder>()

  fun <T, A : ArgBuilder> configStub(argBuilder: A): Config<T, A> = StubConfigImpl(argBuilder)
}

interface Config<T, A : ArgBuilder> {
  fun config(): A
}

interface ArgBuilder {
  fun addArg(name: String, value: Any): ArgBuilder
  fun <T> build(): Stub<T>

  companion object {
    fun <T, A: ArgBuilder> create(): ArgBuilder = InternalImplementation<T, A>() as A
  }
}

In order to have polymorphism for different fields on types implementing an interface yet requiring different arguments so I can declare them like this:

class OrgPostsQuery(
  amount: Int = 100, 
  from: Date = Date.from(Instant.now())) : Model<Organization> {

  val posts by super.model.config()
    .first(1000)
    .since(from)
    .build()

}

(There's a separate set of interfaces for List<T> for a field like posts in the example but I left that out for brevity)

What am I doing incorrectly? Or is this not possible?

1

1 Answers

3
votes

In order to be able to override a generic val with subtypes of the type arguments, you need to use an out-projection.

Change Config<String, BasePostsArgs> to Config<String, out BasePostsArgs> in both interfaces and the abstract class. Then you will be able to use a subtype of BasePostArgs where BasePostArgs were expected.

interface Actor : Type {
    val posts: Config<String, out BasePostsArgs>
}

interface SomeConflict : Type {
    val posts: Config<String, out BasePostsArgs>
}

object Organization : Type, SomeConflict, Actor {
    override val posts: Config<String, Organization.PostsArgs> = configStub(PostsArgs())

    /* ... */
}

The error you get is caused by the fact that, if some type A is invariant on its type parameter, then A<T1> and A<T2> are never subtypes of each other if T1 and T2 are not the same type (in your case, T1 := BasePostsArgs, T2 := Organization.PostsArgs). But when you use an out-projection, A<T1> becomes a subtype of A<T2> whenever T1 is a subtype of T2. And you can override a val with a subtype of the property in the base type (but that does not work for vars).