FrMan
FrMan

Reputation: 31

How to call methods in a class of generic type T on variables of the same type but that are not declared Kotlin

I'd like variable f to be as the same type of the generic parameter F, but I can't declare f in the constructor of the class.

  class myClass<F: myInterface>(
     private val v1: String,
     private val v2: String,
  ){
     f.methodOfMyInterface()
  }

Upvotes: 0

Views: 374

Answers (2)

gidds
gidds

Reputation: 18617

You can simply declare f as being of type F, e.g.:

class MyClass<F: MyInterface>(/*…*/) {
   val f: F = /*…*/
}

It doesn't matter that F isn't known at compile-time: the compiler will ensure it all works out at run-time.

(If you're interested in how it's implemented: in Java and Kotlin, generics like this are implemented by type erasure, which means that the compiled bytecode knows nothing about F.  It simply uses the relevant upper bound on the type, which in this case is MyInterface.  The compiler makes sure that it's only used in a type-safe way.)

When creating an instance, you'll need to give the compiler a clue as to what type F is.  You could do it directly, e.g.:

val myClass = MyClass<SomethingImplementingMyInterface(/*…*/)

Or you could specify the resulting type:

val myClass: MyClass<SomethingImplementingMyInterface> = MyClass(/*…*/)

Or one of the parameters could mention that type.  For example, if the class were:

class MyClass<F: MyInterface>(val f: F)

then the compiler could infer it from that:

val myClass = MyClass(SomethingImplementingMyInterface())

(Note: I'd be very wary about using reflection.  It's great for tools, plug-ins, and other special cases; but for general application code, it's rarely necessary, as well as being slow and usually not type-safe.  There's usually a better design that avoids it.)

Upvotes: 1

Mateusz Herych
Mateusz Herych

Reputation: 1605

I'm not fully certain that this fulfils your needs, but this is probably as close as you can get:

interface Test

class ImplTest : Test

class Data<T : Test>(factory: () -> T) {
  val t: T = factory()
}

inline fun <reified T : Test> data(): Data<Test> {
  return Data { T::class.java.newInstance() }
}

Then you can invoke:

data<ImplTest>()

And this will return you a Data object, which will have an instance of ImplTest assigned to the t field.

Assumptions:

  • You're fine with reflection.
  • You can assume that whatever T is has an empty constructor.

Upvotes: 0

Related Questions