Kotlin – 检查通用参数是否可选?
我正在写这个通用的方法来从Firebase中获取数据? 在某些情况下返回空有效,在其他情况下是不是有可能检查泛型参数是否可以为空?
前
reference.obsrveObject(User.class)
应该抛出如果null
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>
跟踪可空性,因为它们只代表一个类。 但是, isMarkedNullable
可以表示“一个具有可选类型参数的类,并且具有可空性”,并且具有isMarkedNullable
属性。
您可以使用KClass
类型参数来获取泛型类型的KClass
,但不能获得(Kotlin 1.1版本) KType
。 但是,您仍然可以检查泛型类型是否可以为null( 可空类型:Kotlin ):
inline fun <reified T> isMarkedNullable(): Boolean { try { null as T return true } catch (e: TypeCastException) { return false } }
然后,可以调用isMarkedNullable<User>()
和isMarkedNullable<User?>()
,分别返回false
和true
。
有了这个,只要你可以标记obsrveObject
为inline
,你可以“检查通用参数是否可选”:
inline fun <reified T> DatabaseReference.obsrveObject(): Observable<T> { return observeSingleEvent().map { snapshot -> if (snapshot != null) { snapshot.getValue(T::class.java) } else if (isMarkedNullable<T>()) { null as T } else { throw Exception("") } } }
用法:
databaseReference.obsrveObject<User>() // Observable<User> databaseReference.obsrveObject<User?>() // Observable<User?>
如果你不能使用一个内联函数(因此需要通用类型参数),那么你需要找到一个获得KType
。
您可以从KCallable<R>
的returnType
获得KCallable<R>
但也可以使用createType
从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)