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?>() ,分别返回falsetrue


有了这个,只要你可以标记obsrveObjectinline ,你可以“检查通用参数是否可选”:

 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>但也可以使用createTypeKClass<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)