1
votes

I have a few classes that do almost the same thing, so I wanted to create an abstract class to remove the duplication. The simplest way to see is through the small example below:

I have the classes A and B as:

class A {
  fun create(
        request: RA,
      ): ResponseEntity<RA> {
        return foo(RA::class)
  }

}

class B {
  fun create(
        request: RB,
    ): ResponseEntity<RB> {
        return foo(RB::class)
  }

}

Where RA and RB both inherit from RX.

I want to create an abstract class to remove this duplication. So I tried something like this:

abstract class C <R: RX> {
  fun create(
        request: R,
    ): ResponseEntity<R> {
        return foo(getKClass())
  }

  abstract fun getKClass() : KClass<R>
}

class A : C() {

  fun aMethod() {
    println("Hello world!")
  }

  override fun getKClass() : KClass<A> {
    return A::class
  }
}

class B : C() {

  fun bMethod() {
    println("Hello world2!")
  }

  override fun getKClass() : KClass<B> {
    return B::class
  }

}

So that then Class A and Class B are able to extend Class C and the duplication is removed. The issue is that since R in the Class C is generic I get the error cannot use R as reified type parameter. Use a class instead. I tried a few things such as making the create function inline and using reified types in Class C. Is there anything else I should try or just keep the duplication?

I just noticed this answer from this user (Kotlin get generic type class using reified), so I tried to have a function in class A and class B, that returned the KClass and then use that in the abstract class C, but also to no avail.

Thanks

1
Could you attach the code with KClass?Andrei Tanana

1 Answers

1
votes

As the answer to the question you linked states:

reified only works when the type information is known at the call site at compile time, which is not the case here.

If you want to be able to do that, you need to store a Class or KClass as a property of your class, or indeed to create a virtual method for obtaining the Class or KClass instance for each derived class.

So you could you implement the first suggestion (store a Class or KClass as a property of your class) with something like this:

abstract class C<R : RX>(private val clazz: KClass<R>) {
    fun create(request: R): ResponseEntity<R> {
        return foo(clazz)
    }
}

class A : C<RA>(RA::class)

class B : C<RB>(RB::class)

If you prefer the second suggestion (create a virtual method for obtaining the Class or KClass) then replace withe clazz constructor parameter with a protected abstract method or property instead. (Though a constructor parameter here is probably a bit less code to achieve the same result and is arguably the more idiomatic way to do it.)

I've assumed that you have an RA and RB as well as A and B. Is that correct? I wasn't quite clear from your two code snippets above.