How to get generic parameter class in Kotlin

后端 未结 5 790
佛祖请我去吃肉
佛祖请我去吃肉 2020-12-05 09:30

Firebase\'s snapshot.getValue() expects to be called as follows:

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

However I would like t

相关标签:
5条回答
  • 2020-12-05 09:44

    you can get its class type like this

    snapshot?.getValue((this.javaClass
                            .genericSuperclass as ParameterizedType)
                            .actualTypeArguments[0] as Class<T>)
    
    0 讨论(0)
  • 2020-12-05 09:44

    Using typeOf is another option. (Added in Kotlin 1.3)

    Example:

    @OptIn(ExperimentalStdlibApi::class)
    inline fun <reified T> someMethod(data: T) {
    
        val typeClassifier = typeOf<T>().classifier
    
        if (typeClassifier == List::class) {
            //Do something
        }
    
    }
    
    0 讨论(0)
  • 2020-12-05 09:46
    1. Using class comparison. See https://stackoverflow.com/a/61756761/2914140.

       inline fun <reified T> SharedPreferences.getData(key: String, defValue: Any? = null): T? =
           when (T::class) {
               Integer::class -> {
                   val value = getInt(key, (defValue as? Int) ?: 0) as T
                   if (value == 0) defValue as? T else value
               }
               Long::class -> {
                   val value = getLong(key, (defValue as? Long) ?: 0L) as T
                   if (value == 0) defValue as? T else value
               }
               Boolean::class -> {
                   val value = getBoolean(key, (defValue as? Boolean) ?: false) as T
                   if (value == false) defValue as? T else value
               }
               String::class -> getString(key, defValue as? String) as T?
               else -> throw IllegalStateException("Unsupported type")
           }
      
    2. Using isAssignableFrom. See https://dev.to/cjbrooks12/kotlin-reified-generics-explained-3mie.

       inline fun <reified T : Number> SharedPreferences.getData(key: String): T? {
           val cls = T::class.java
           return if (cls.isAssignableFrom(Integer::class.java)) {
               getInt(key, 0) as T
           } else if (cls.isAssignableFrom(Long::class.java)) {
               getLong(key, 0) as T
           } else {
               throw IllegalStateException("Unsupported type")
           }
       }
      

    For Double in SharedPreferences see https://stackoverflow.com/a/45412036/2914140.

    Use:

    val s: String? = preferences.getData("key", "")
    val i = preferences.getData<Int>("key")
    
    0 讨论(0)
  • 2020-12-05 09:56

    For a class with generic parameter T, you cannot do this because you have no type information for T since the JVM erases the type information. Therefore code like this cannot work:

    class Storage<T: Any> {
        val snapshot: Snapshot? = ...
    
        fun retrieveSomething(): T? {
            return snapshot?.getValue(T::class.java) // ERROR "only classes can be used..."
        }
    }
    

    But, you can make this work if the type of T is reified and used within an inline function:

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

    Note that the inline function if public can only access public members of the class. But you can have two variants of the function, one that receives a class parameter which is not inline and accesses private internals, and another inline helper function that does the reification from the inferred type parameter:

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

    You can also use KClass instead of Class so that callers that are Kotlin-only can just use MyClass::class instead of MyClass::class.java

    If you want the class to cooperate with the inline method on the generics (meaning that class Storage only stores objects of type T):

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

    The link to reified types in inline functions: https://kotlinlang.org/docs/reference/inline-functions.html#reified-type-parameters

    0 讨论(0)
  • 2020-12-05 10:06

    What you need is reified modifier for your generic param, you can read about it here. https://kotlinlang.org/docs/reference/inline-functions.html#reified-type-parameters So if you do something like that:

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

    you will get name of the actual caller class, not "Object".

    0 讨论(0)
提交回复
热议问题