如何在Kotlin中获取泛型参数类

Firebase的 snapshot.getValue() 预计会被调用如下:

snapshot?.getValue(Person::class.java)

但是我想用 Person 替换通过类声明传递给类的泛型参数,即

class DataQuery<T : IModel>

并使用该泛型参数执行以下操作:

snapshot?.getValue(T::class.java)

但当我尝试我得到一个错误说明

只能在类文字的左侧使用类

是否可以像在C#中那样为泛型参数提供类约束,或者是否可以使用其他语法来获取泛型参数的类型信息?

回答(3)

2 years ago

对于具有泛型参数T的类,您无法执行此操作,因为您没有T的类型信息,因为JVM会删除类型信息 . 因此这样的代码不起作用:

class Storage<T: Any> {
    val snapshot: Snapshot? = ...

    fun retrieveSomething(): T? {
        return snapshot?.getValue(T::class.java) // ERROR "only classes can be used..."
    }
}

但是,如果T的类型被内联并在内联函数中使用,则可以使其工作:

class Storage {
    val snapshot: Snapshot? = ...

    inline fun <reified T: Any> retrieveSomething(): T? {
        return snapshot?.getValue(T::class.java)
    }
}

请注意,如果public,则内联函数只能访问该类的公共成员 . 但是你可以有两个函数变体,一个接收不是内联的类参数并访问私有内部,另一个内联辅助函数根据推断的类型参数进行相关:

class Storage {
    private val snapshot: Snapshot? = ...

    fun <T: Any> retrieveSomething(ofClass: Class<T>): T? {
        return snapshot?.getValue(ofClass)
    }

    inline fun <reified T: Any> retrieveSomething(): T? {
        return retrieveSomething(T::class.java)
    }
}

您也可以使用 KClass 而不是 Class ,这样只有Kotlin的调用者可以使用 MyClass::class 而不是 MyClass::class.java

如果您希望该类与泛型上的内联方法配合使用(意味着类 Storage 仅存储 T 类型的对象):

class Storage <T: Any> {
    val snapshot: Snapshot? = ...

    inline fun <reified R: T> retrieveSomething(): R? {
        return snapshot?.getValue(R::class.java)
    }
}

内联函数中指定类型的链接:https://kotlinlang.org/docs/reference/inline-functions.html#reified-type-parameters

2 years ago

你需要的是你的通用参数的reified修饰符,你可以在这里阅读它 . https://kotlinlang.org/docs/reference/inline-functions.html#reified-type-parameters所以,如果你这样做:

inline fun <reified T : Any>T.logTag() = T::class.java.simpleName

您将获得实际调用者类的名称,而不是“对象” .

2 years ago

你可以得到这样的类类型

snapshot?.getValue((this.javaClass
                        .genericSuperclass as ParameterizedType)
                        .actualTypeArguments[0] as Class<T>)