Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get class of generic type parameter in Kotlin

I would like get the class property from a generic type T. I've decided to extend to Any but I'm getting an error. https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/index.html#extension-properties

I have the following code:

class FirebaseDBRepo<T : Any>(val child:String) {

private var callback: FirebaseDatabaseRepositoryCallback<T>? = null
private val ref: DatabaseReference
private val listener = object : ValueEventListener {
    override fun onDataChange(dataSnapshot: DataSnapshot) {

        //T::class.java is showing the error cannot use t as reified type parameter use class instead

        val gameDS = dataSnapshot.getValue(T::class.java)
        callback!!.onSuccess(gameDS!!)
    }

    override fun onCancelled(databaseError: DatabaseError) {

    }
}

init {
    ref = FirebaseDatabase.getInstance().reference.child(child)
}


fun addListener(callback: FirebaseDatabaseRepositoryCallback<T>) {
    this.callback = callback
    ref.addValueEventListener(listener)
}

fun removeListener() {
    ref.removeEventListener(listener)
}

}
like image 894
Ricardo Avatar asked Sep 21 '18 15:09

Ricardo


People also ask

How do you indicate that a class has a generic type parameter?

A generic type is declared by specifying a type parameter in an angle brackets after a type name, e.g. TypeName<T> where T is a type parameter.

How do I know my Kotlin class type?

Basically, the is operator is used to check the type of the object in Kotlin, and “!is” is the negation of the “is” operator. Kotlin compiler tracks immutable values and safely casts them wherever needed. This is how smart casts work; “is” is a safe cast operator, whereas an unsafe cast operator is the as operator.

What is :: class in Kotlin?

:: is just a way to write a lambda expression basically we can use this to refer to a method i.e a member function or property for example class Person (val name: String, val age: Int) Now we can write this to access the person which has the maximium age.


1 Answers

You can only get the class on reified variables. The same thing happens in java, but with a slightly different message:

public <T> void x(){
    T t = T.class.newInstance();
}

In Java, you'd solve this like:

public <T> void x(Class<T> cls){
    T t = cls.newInstance();
}

The same applies to Kotlin, and any calls. You'd need to get a class instance in most cases. However, Kotlin supports reified generics using a keyword, but only on inline generic functions. You could pass a class, but in functions, it's really easy just using the reified keyword.

As in you can't declare a class with reified generics, which means this is invalid:

class SomeClass<reified T>

But it is valid for inline functions, meaning you can do:

inline fun <reified T> someFunction()

So you have two options. But since you extend a listener, the first option of adding the generics to the function isn't an option. You can't override a non-generic method with generics. It won't compile.

Which leaves the second option, which unfortunately is rather hackish; passing the class to the constructor. So it should look like this:

class FirebaseDBRepo<T : Any>(val child: String, private val cls: Class<T>) {

Now, I don't use Firebase, so I have no clue what classes you'd pass, so for this next example, I just use String.

Kotlin supports some type minimization without going over to raw types. This:

val t = FirebaseDBRepo<String>("", String::class.java)

Could be shortened to this:

val t = FirebaseDBRepo("", String::class.java)

The inferred type in both cases is FirebaseDBRepo<String>.

like image 174
Zoe stands with Ukraine Avatar answered Sep 26 '22 00:09

Zoe stands with Ukraine