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

时间:2017-08-19 01:40:51

标签: android generics kotlin nullable

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

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("")
        }
    }
}

1 个答案:

答案 0 :(得分:4)

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

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

您可以使用reified type parameters获取通用类型的KClass,但是您无法获得(从Kotlin 1.1开始)KType。但是,您仍然可以检查泛型类型是否可以为null is Tnull as T一样(感谢nullable reified type : Kotlin sositetry包裹在catch中<{1}} / 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>获得KType,但您也可以使用pointing outKClass<T>创建User::class.createType(nullable = false) // User User::class.createType(nullable = true) // User?

obsrveObject

此处的类型类型为createType,因此根据您使用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)

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

function parseChart($str){
    $lines = explode(PHP_EOL,$str); // split lines into array
    $line_count = count($lines);

    $current_label = null;
    $current_object = null;

    $data = array(); // output data

    for($i=0;$i<$line_count;$i++){

        $line = trim($lines[$i]); // remove spaces & tabs from start and end of the current line

        if(strpos($line, '[')===0){ // if line starts with [

            $current_label = substr($line,1,-1); // set current label

        }else if(strpos($line, '{')===0){ // if line starts with {

            $current_object = array(); // create new object

        }else if(strpos($line, '}')===0){ // if line starts with }

            if(isset($current_label))
                $data[$current_label] = $current_object; // write out new object into the outpdata with the label that has been set beforehand

        }else{

            $equals_index = strpos($line, '='); // get index of =
            if($equals_index!==false || !isset($current_object)){ // no = found or no label set -> ignore line

                $key = trim(substr($line,0,$equals_index));
                $value = trim(substr($line,$equals_index+1));

                $current_object[$key] = $value;

            }

        }
    }
    return $data;
}