Kotlin反射
2022-03-15
7 min read
kotlin可以直接使用java的反射,如果要使用kotlin的反射,那么需要单独引入kotlin的反射库
implementation "org.jetbrains.kotlin:kotlin-reflect"
那么这和Java对比的话有什么区别
Java
-
优点:无需引入额外依赖,首次使用速度相对较快(这是因为它的信息都在虚拟机内)。
-
缺点:无法访问Kotlin语法特性,需要对Kotlin生成的字节码足够了解(这是因为Kotlin程序编译完之后也是个Java类,所以在Java反射视角下看到的Kotlin编译的状态和一般Java类无异,这个时候如果要通过反射访问Kotlin类的一些方法属性,必须要知道它编译什么样的字节码)
kotlin
-
优点:支持访问Kotlin几乎所有特性,API设计兼容性更好。
-
缺点:额外引入了一个Kotlin反射库(这个库2.5M左右,编译后400KB),首次调用会慢一些,这是因为Kotlin的反射信息是写到Metadata这个注解里面的(通过查看字节码可以看到,如下),里面的数据通过Protobuf的数据格式序列化二进制写入,所以首次反射调用有一个获取注解并反序列化的过程)。
@Metadata(
mv = {1, 6, 0},
k = 1,
d1 = {"\u0000\u001a\n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0002\b\u0003\u0018\u0000*\n\b\u0000\u0010\u0001 \u0000*\u00020\u00022\u00020\u0003B\u0005¢\u0006\u0002\u0010\u0004J\u0013\u0010\u0005\u001a\u00020\u00062\u0006\u0010\u0007\u001a\u00028\u0000¢\u0006\u0002\u0010\b¨\u0006\t"},
d2 = {"Lcom/qisan/kotlinstu/Dustbin;", "T", "Lcom/qisan/kotlinstu/Waste;", "", "()V", "put", "", "t", "(Lcom/qisan/kotlinstu/Waste;)V", "KotlinStu.app"}
)
一些在Kotlin反射中的常用Api:
var cls: KClass<String> = String::class
//KClass转为java的Class<String>
var clsJava: Class<String> = cls.java
//java的Class<String>转为KClass
cls = clsJava.kotlin
//获取定义在String类的属性,返回的是Collections
val properties = cls.declaredMemberProperties
//返回这个类中的所有方法
cls.declaredFunctions
//返回这个类中的所有非静态非扩展的函数
cls.declaredMemberFunctions
//返回这个类中中的扩展函数,注意看下面的类A
cls.declaredMemberExtensionFunctions
cls.isAbstract//是否是抽象类
cls.isCompanion//是否是伴生对象
cls.nestedClasses//获取当前类的内部类
cls.objectInstance//如果是object,可以通过这个方法获取
1.declaredMemberExtensionFunctions的式例
class A {
fun test(){
//这里的反射属性通过这个是可以获取到这个String.pass的扩展方法
A::class.declaredMemberExtensionFunctions
//但是通过String是获取不到
String::class.declaredMemberExtensionFunctions
"".pass()
}
fun String.pass(){
}
}
在A的扩展函数中能够获取到String.pass的扩展方法,但是String::class.declaredMemberExtensionFunctions是没有的,而在顶级的扩展函数中是构建成静态方法的.
fun String.pass() {
println("test")
}
//ktx.kt 对应到的java代码
@Metadata(
mv = {1, 6, 0},
k = 2,
d1 = {"\u0000\f\n\u0000\n\u0002\u0010\u0002\n\u0002\u0010\u000e\n\u0000\u001a\n\u0010\u0000\u001a\u00020\u0001*\u00020\u0002¨\u0006\u0003"},
d2 = {"pass", "", "", "Arount.app"}
)
public final class KtxKt {
public static final void pass(@NotNull String $this$pass) {
Intrinsics.checkNotNullParameter($this$pass, "$this$pass");
String var1 = "test";
System.out.println(var1);
}
}
2.KType的获取
fun testKType(){
val mapType = typeOf<Map<String,Int>>()
mapType.arguments.forEach {
print(it)
}
/**
* 输出结果:
* kotlin.String
* kotlin.Int
* KType可以找到泛型实参
*/
}
3.获取接口方法的返回的泛型实例参数类型
interface Api{
fun getUsers():List<UserModel>
}
data class UserModel(var id:Int,var name:String)
fun testGetFunParamsType(){
Api::class.declaredFunctions.first { it.name == "getUsers" }
.returnType.arguments.forEach {
print(it)
}
//两者相同
Api::getUsers.returnType.arguments.forEach {
print(it)
}
}
4.获取实例对象上的泛型约束类型
abstract class SuperType<T>{
val typeParameter by lazy {
//this表示的是子类的实例,因为抽象类不能用于创建实例,只能背当做父类被子类继承
this::class.supertypes.first().arguments.first().type
//如果有多个子类继承 比如class SubType2:SubType(){}
//这个时候this::class.supertype会空,因为SubType没有泛型实参这时候要使用:
this::class.allSupertypes.first().arguments.first().type
}
}
open class SubType:SuperType<String>()
class SubType2:SubType()
fun testGetParentType(){
val subType = SubType()
subType.typeParameter.let {
print(it)
//kotlin.String
}
}
场景引用-深层拷贝
data class Person(val id: Int, val name: String, val group: Group)
data class Group(val id: Int, val name: String)
/**
* 深层数据拷贝
*/
fun <T:Any> T.deepCopy():T{
if(!this::class.isData){
return this
}
return this::class.primaryConstructor!!.let { primaryConstructor->
primaryConstructor.parameters.associate { parameter ->
val value =
(this::class as KClass<T>).memberProperties.first { it.name == parameter.name }
.get(this)
if ((parameter.type.classifier as? KClass<*>)?.isData == true) {
parameter to value?.deepCopy()
} else {
parameter to value
}
}
.let {
primaryConstructor.callBy(it)
//primaryConstructor是KFunction<T>类型 KFunction<T>里面有callBy方法 只要传一个map,返回调用者的参数类型T
}
}
}
private fun testCopy() {
val group = Group(1, "11111")
val person = Person(1, "haha", group)
//浅层拷贝实际上就是new了一个新的对象,并将当前类的属性传入,那么group就传入的是同一个了
val copyPerson = person.copy()
//也就是说通过copy的方法,实现的拷贝中group还是指向的同一个,那么相应的修改就会造成同步的修改
Log.e(">>>>>>>", (person === copyPerson).toString()) //false
Log.e(">>>>>>>", (person.group === copyPerson.group).toString()) //true
//深层拷贝的第一次是真的慢
val startTime = System.currentTimeMillis()
val deepCope = person.deepCopy()
val endTime = System.currentTimeMillis()
Log.e(">>>>>>>>", "cost time:${endTime - startTime}")
Log.e(">>>>>>>", (person === deepCope).toString()) //false
Log.e(">>>>>>>", (person.group === deepCope.group).toString()) //false
}