android - Kotlin - 检查泛型参数是否可选?

标签 android generics kotlin nullable

我正在编写这个通用方法来从 firebase 获取数据?在某些情况下返回 null 是有效的,在其他情况下则无效,是否可以检查泛型参数是否可为 null?

reference.obsrveObject(User.class)

如果为空则抛出

reference.obsrveObject(User?.class)

应该用空值调用onNext

fun DatabaseReference.observeSingleEvent(): Observable<DataSnapshot?> {
    return Observable.create { subscriber ->
        val valueEventListener = object: ValueEventListener {
            override fun onDataChange(snapshot: DataSnapshot?) {
                subscriber.onNext(snapshot)
                subscriber.onCompleted()
            }

            override fun onCancelled(error: DatabaseError?) {
                subscriber.onError(FirebaseDatabaseThrowable(error))
            }
        }

        addListenerForSingleValueEvent(valueEventListener)
    }
}

fun <T>DatabaseReference.obsrveObject(clazz: Class<T>): Observable<T> {
    return observeSingleEvent().map { snapshot ->
        if (snapshot != null) {
            snapshot.getValue(clazz)
        }
        else {
            // if clazz is nullable return null
            // if clazz is not nullabel throw
            throw Exception("")
        }
    }
}

最佳答案

注意:我还没有亲自使用过 Firebase,因此下面的一些示例可能无法编译,但应该足够接近。

都不是 Class<T> 也不 KClass<T> 跟踪可空性,因为它们只代表一个类。 KType ,但是,可以表示“具有可选类型参数和可空性的类”并且具有 isMarkedNullable 属性(property)。

您可以使用 reified type parameters得到 KClass对于通用类型,但您无法获得(从 Kotlin 1.1 开始)KType .但是,您仍然可以使用 null is T 检查泛型类型是否可为空 ( nullable reified type : Kotlin ) (感谢 sositepointing out 不需要将 null as T 包裹在 try/catch 中)。


有了这个,只要标记obsrveObject就可以了作为inline ,您可以“检查泛型参数是否可选”:

inline fun <reified T> DatabaseReference.obsrveObject(): Observable<T> {
    return observeSingleEvent().map { snapshot ->
        if (snapshot != null) {
            snapshot.getValue(T::class.java)
        } else if (null is T) {
            null as T
        } else {
            throw Exception("")
        }
    }
}

用法:

databaseReference.obsrveObject<User>() // Observable<User>
databaseReference.obsrveObject<User?>() // Observable<User?>

如果您不能使用内联函数(因此不能使用具体化的类型参数),那么您需要找到一种方法来获得 KType .

你可以得到一个KType来自 returnTypeKCallable<R>但你也可以创建一个 KType来自KClass<T>使用 createType :

User::class.createType(nullable = false)    // User
User::class.createType(nullable = true)     // User?

这里的类是类型的 classifier 所以取决于你如何使用 obsrveObject您可以将其参数类型从 Class<T> 更改为至 KCallable<T> .您可以将其更改为 KType直接并根据需要创建实例,但我猜你正在抓取 clazz目前来自属性的返回类型,所以我会选择 KCallable<T> :

fun <T : Any> DatabaseReference.obsrveObject(callable: KCallable<T>): Observable<T?> {
    val kType = callable.returnType
    val kClass = kType.classifier as KClass<T>
    val clazz = kClass.java
    return observeSingleEvent().map { snapshot ->
        if (snapshot != null) {
            snapshot.getValue(clazz)
        } else if (kType.isMarkedNullable) {
            null
        } else {
            throw Exception("")
        }
    }
}

然后您可以使用对可调用对象(属性、函数等)的引用来调用它:

databaseReference.obsrveObject(session::user)

关于android - Kotlin - 检查泛型参数是否可选?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45766885/

相关文章:

java - 使用通用类型参数类

java - 上传到 Playstore 时缺少 Android 图标

.net - 使用带有泛型类型参数的异常是好是坏

java - 错误 :com. android.dex.DexIndexOverflowException : method ID not in [0, 0xffff]:65536

android - 如何获取 map 信息窗口 View 的屏幕坐标

java - 何时在 Java 中使用无界通配符

java - 参数化类 'ABC' 的原始使用

java - Kotlin 中的 Java Scanner 相当于什么?

java - Spring Boot EnvironmentPostProcessor 未加载 application.properties

Kotlin 将时间戳转换为日期时间