Kotlin - 检查通用参数是否可选?

问题描述 投票:2回答:1

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

reference.obsrveObject(User.class)

应该抛出null

reference.obsrveObject(User?.class)

应该使用null值调用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("")
        }
    }
}
android generics kotlin nullable
1个回答
4
投票

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

Class<T>KClass<T>都没有跟踪可空性,因为他们只代表一个阶级。然而,KType可以表示“具有可选类型参数的类,加上可空性”并且具有isMarkedNullable属性。

你可以使用reified type parameters来获得通用类型的KClass,但你不能得到(从Kotlin 1.1开始)KType。但是,您仍然可以检查泛型类型是否可以为空(nullable reified type : Kotlin)与null is T(感谢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上的returnType获得KCallable<R>,但你也可以使用KTypeKClass<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)
© www.soinside.com 2019 - 2024. All rights reserved.