Kotlin反射:如何检查类属性是否为特定类型的ArrayList
Kotlin反射检查属性是否为特定泛型ArrayList类型
刚接触Kotlin和Java,想通过反射检查属性类型是否为ArrayList<Pair<String, ByteArray>>,现有代码如下:
class DataUploadFormData { var files: ArrayList<Pair<String, ByteArray>> = arrayListOf() var collection: String? = null } // DataUploadFormData type is passed here as T suspend inline fun <reified T : Any> mapFormData(data: MultiPartData): T? { val formData: FormData = getFormData(data) val model: T? = getValue() var fields = T::class.memberProperties fields.forEach { field -> if (field is KMutableProperty<*>) { setFieldValue(field, formData, model) } } return model } @OptIn(ExperimentalStdlibApi::class) fun <T> setFieldValue(field: KMutableProperty<*>, formData: FormData, model: T?) { var value: Any? = null if (field.returnType.javaType == String::class.createType().javaType || field.returnType.javaType == Int::class.createType().javaType || field.returnType.javaType == Double::class.createType().javaType ) { value = formData.fields.firstOrNull(predicate = { it.first == field.name })?.second } else if ( // check if field.returnType is of ArrayList<Pair<String, ByteArray>> type? ) { value = formData.files } field.setter.call(model, value) }
目前简单数据类型的检查已经正常工作,但不清楚如何检查复杂泛型类型。尝试过实例化属性用is判断,但无法通过field.returnType.javaType::class.createInstance()实例化;用field.returnType.classifier is ArrayList<*>判断返回false。
解决方案
要准确判断泛型类型,需要利用Kotlin反射中KType的泛型参数信息——类型擦除会导致直接通过javaType无法获取泛型细节。可以按照以下步骤判断:
- 先判断属性返回类型的分类器(classifier)是否为
ArrayList::class - 再获取
ArrayList的泛型参数,检查是否为Pair<String, ByteArray>:- 判断该泛型参数的分类器为
Pair::class - 检查
Pair的两个类型参数分别为String::class和ByteArray::class
- 判断该泛型参数的分类器为
修改后的setFieldValue函数代码如下:
@OptIn(ExperimentalStdlibApi::class) fun <T> setFieldValue(field: KMutableProperty<*>, formData: FormData, model: T?) { var value: Any? = null if (field.returnType.javaType == String::class.createType().javaType || field.returnType.javaType == Int::class.createType().javaType || field.returnType.javaType == Double::class.createType().javaType ) { value = formData.fields.firstOrNull(predicate = { it.first == field.name })?.second } else if ( // 第一步:判断是ArrayList类型 field.returnType.classifier == ArrayList::class && field.returnType.arguments.size == 1 ) { val listElementType = field.returnType.arguments[0].type ?: return // 第二步:判断ArrayList的元素是Pair类型,且参数匹配 if (listElementType.classifier == Pair::class && listElementType.arguments.size == 2) { val firstArg = listElementType.arguments[0].type ?: return val secondArg = listElementType.arguments[1].type ?: return if (firstArg.classifier == String::class && secondArg.classifier == ByteArray::class) { value = formData.files } } } field.setter.call(model, value) }
关键说明
- 用
field.returnType.classifier == ArrayList::class替代is ArrayList<*>:classifier返回的是KClass<*>实例,直接用==判断是否为目标类的KClass更准确,is用于判断实例类型,此处不适用。 - 必须通过
KType.arguments获取泛型参数:类型擦除后,javaType无法保留泛型信息,而Kotlin的KType会在编译期保留这些信息(前提是反射的类型是reified或者有完整的类型签名)。
内容的提问来源于stack exchange,提问作者Andrew Krachko
相关产品推荐
相关产品推荐

