Как получить класс универсального параметра типа в Kotlin

Я хотел бы получить свойство класса из универсального типа T, Я решил расширить до Any но я получаю ошибку. https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/index.html

У меня есть следующий код:

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)
}

}

3 ответа

Решение

Вы можете получить класс только по переменным. То же самое происходит в Java, но с немного другим сообщением:

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

В Java вы можете решить это следующим образом:

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

То же относится и к котлину, и к любым звонкам. Вам нужно будет получить экземпляр класса в большинстве случаев. Тем не менее, Kotlin поддерживает обобщенные обобщенные типы, используя ключевое слово, но только для встроенных обобщенных функций. Вы можете передать класс, но в функциях это действительно легко, просто используя ключевое слово reified.

Так как вы не можете объявить класс с улучшенными обобщениями, это означает, что это недопустимо:

class SomeClass<reified T>

Но это верно для встроенных функций, то есть вы можете сделать:

inline fun <reified T> someFunction()

Итак, у вас есть два варианта. Но так как вы расширяете слушателя, первый вариант добавления обобщений в функцию не вариант. Вы не можете переопределить неуниверсальный метод с помощью дженериков. Это не скомпилируется.

Что оставляет второй вариант, который, к сожалению, довольно хакерский; передача класса конструктору. Так должно выглядеть так:

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

Теперь я не использую Firebase, поэтому я понятия не имею, какие классы вы будете проходить, поэтому для следующего примера я просто использую String,

Kotlin поддерживает минимизацию некоторых типов, не переходя к необработанным типам. Это:

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

Можно сократить до этого:

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

Предполагаемый тип в обоих случаях FirebaseDBRepo<String>,

Поскольку вы работаете на JVM, стирание типов - это вещь. Это означает (в упрощенном виде), что во время компиляции генерики просто игнорируются. Следовательно, вы не можете получить класс T, так как JVM даже не знает, что вы подразумеваете под "T".

Kotlin использует хитрый трюк, чтобы обойти это ограничение в некоторых случаях. Когда вы используете встроенные функции, компилятор не вызывает функцию, которую вы определили, а вместо этого копирует все тело в место, где вы его вызвали. Это можно сделать только для встроенных функций. Не занятия.

Существует обходной путь: просто добавьте private val classT: Class<T>в конструктор и использовать параметр вместо этого!

Может быть, уже слишком поздно, но вы можете получить адрес памяти из универсального класса.

попробуй использовать:

объект: GenericTypeIndicator<"T>() {}

чтобы получить адрес памяти из общего значения ur.

Выглядит это тогда так:

      val gameDS = dataSnapshot.getValue(object: GenericTypeIndicator<"T">(){}

Но вам нужно указать свой genericType без ""

Возможно, это решение для вас.

Другие вопросы по тегам