Kotlin反射

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
    }