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

ary*_*axt 2 generics android nullable kotlin

我正在编写此通用方法以从Firebase获取数据?在某些情况下,返回空值是有效的,在其他情况下是无效的,是否可以检查泛型参数是否可为空?

reference.obsrveObject(User.class)
Run Code Online (Sandbox Code Playgroud)

如果为null,则应抛出

reference.obsrveObject(User?.class)
Run Code Online (Sandbox Code Playgroud)

应该用空值调用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("")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

mfu*_*n26 5

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

因为它们仅代表一个类,所以也Class<T>没有KClass<T>跟踪可为空性。KType但是,A 可以表示“具有可选类型参数以及可空性的类”并具有一个isMarkedNullable属性。

您可以使用修饰的类型参数来获取KClass泛型类型的,但不能(从Kotlin 1.1开始)获取a KType。但是,你仍然可以检查是否泛型类型为空(null的物化类型:科特林)与null is T(感谢sosite对于指出的是包裹null as Ttry/ catch是不是必要的)。


这样,只要您可以标记obsrveObjectinline,就可以“检查泛型参数是否为可选参数”:

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("")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

databaseReference.obsrveObject<User>() // Observable<User>
databaseReference.obsrveObject<User?>() // Observable<User?>
Run Code Online (Sandbox Code Playgroud)

如果您不能使用内联函数(因此无法使用修饰的类型参数),则需要找到一种获取的方法KType

你可以得到一个KTypereturnTypeKCallable<R>,但你也可以创建一个KTypeKClass<T>使用createType

User::class.createType(nullable = false)    // User
User::class.createType(nullable = true)     // User?
Run Code Online (Sandbox Code Playgroud)

这里的类是类型的,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("")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

databaseReference.obsrveObject(session::user)
Run Code Online (Sandbox Code Playgroud)

  • 我认为检查泛型是否可为空的更简单解决方案是“内联乐趣&lt;reified T&gt; isMarkedNullable()= null为T”。 (3认同)