Kotlin中反射机制的使用语法

  • Post author:
  • Post category:其他


反射机制概述

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类中的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

Java 反射机制的功能

  • 在运行时判断任意一个对象所属的类。
  • 在运行时构造任意一个类的对象。
  • 在运行时判断任意一个类所具有的成员变量和方法。
  • 在运行时调用任意一个对象的方法。
  • 生成动态代理。
  • Java 反射机制的应用场景

  • 逆向代码 ,例如反编译
  • 与注解相结合的框架 例如Retrofit
  • 单纯的反射机制应用框架 例如EventBus
  • 动态生成类框架 例如Gson
  • 获取Class对象

    在Java中,java类被JVM虚拟机加载后,系统就会该类生成一个对应的Class对象,通过该Class对象就可以访问到JVM中的这个类。

    首先先创建一个Human类,在java文件或者kt文件中创建class类都是一样的:

    Human类

    public class Human {
        private String name="Default";
        private int age=18;
        public String code="8888";
    
        public Human(){
    
        }
    
        public Human(String name){
            this.name=name;
        }
    
        public Human(String name,int age){
            this.name=name;
            this.age=age;
        }
    
        public Human(String name,int age,String code){
            this.name=name;
            this.age=age;
            this.code=code;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public void sayHello(){
            System.out.println("Hello");
        }
    
        private void say(String str){
            System.out.println("The Human "+name+" is saying \""+str+"\"");
        }
    
        public String talking(){
            return "Goodbye";
        }
    
        private void doNothing(String noThing){
    
        }
    }
    

  • 使用Class类的forName(String clazzName)静态方法。该方法需要传入字符串参数,该字符串参数的值是某个类的全限定名(必须添加完整包名):
  • val hClass=Class.forName("Human")//获取Human.class

  • 调用某个类的Class属性或KClass属性来获取该类对应的Class对象:
  • val hClass:Class<Human>?=Human::class.java//获取Human类的class属性
    val hClass: KClass<Human>?=Human::class//获取Human类的Kclass属性

    注意:在Kotlin中::符号是引用符号,可以引用属性或类。Class和Kclass类型是不一样,里面的方式类同。

  • 调用某个对象的javaClass属性:
  • val h=Human()
    val hClass:Class<Human>?=h.javaClass

    获取Class对象的成员变量

    在Kotlin中对应于Class有一种包装类型KClass,这两种Class类型的使用方式不一样:

  • 获取Class对象的成员变量
  • val hClass_1=Human::class.java//获取Human类的class属性
    val allFields_1:Array<Field>?=hClass_1.declaredFields//获取class对象的所有属性
    val publicFields_1:Array<Field>?=hClass_1.fields//获取class对象的public属性
    val nameField_1:Field=hClass_1.getDeclaredField("age")//获取class指定属性
    val code_1:Field=hClass_1.getField("code")//获取class指定的public属性

  • 获取KClass对象的成员变量
  •     val hClass_2=Human::class
        val allFields_2=hClass_2.declaredMemberProperties//获取KClass对象的所有属性
        val publicFields_2=hClass_2.memberProperties//获取KClass对象的public属性
        val extensionFields=hClass_2.declaredMemberExtensionProperties//获取KClass对象的扩展成员变量
        val nameField_2=publicFields_2.stream().filter { it.name=="name" }.findAny().get().javaField//获取KClass对象的指定属性,将KProperty属性类型转换后的类型是java中的Field类型

    获取Class对象的方法

  • 获取Class对象的方法
  •     val methods_1 = hClass_1.declaredMethods//获取class对象的所有声明方法
        val allMethods_1 = hClass_1.methods//获取class对象的所有public方法 包括父类的方法
        val method_1 = hClass_1.getMethod("sayHello")//返回class对象中与指定方法名,带指定参数列表的public方法
        val declareMethod_1 = hClass_1.getDeclaredMethod("say", String::class.java)//返回class对象中与指定方法名,带指定参数列表的方法

  • 获取KClass对象的方法
  •     val methods_2 = hClass_2.memberFunctions//获取KClass对象的所有声明方法,包括父类的方法
        val allMethods_2 = hClass_2.declaredMemberFunctions//获取KClass对象的所有声明方法
        val method_2 = methods_2.stream().filter { it.name == "say" }.findAny().get().javaMethod//返回KClass对象中指定方法名的方法,将KFunction方法类型转换后的类型是java中的Method类型

    获取Class对象的构造函数

  • 获取Class对象的构造函数
  •     val allConstructors_1 = hClass_1.declaredConstructors//获取class对象的所有声明构造函数
        val publicConstructors_1 = hClass_1.constructors//获取class对象的所有public构造函数
        val constructor_1 = hClass_1.getDeclaredConstructor(String::class.java)//获取与指定声明参数列表类型匹配的构造函数
        val publicConstructor_1 = hClass_1.getConstructor(String::class.java)//获取与指定声明参数列表类型匹配的public构造函数

  • 获取KClass对象的构造函数
  •     val allConstructors_2 = hClass_2.constructors//获取KClass对象的所有声明构造函数
        val privateConstructors_2 = hClass_2.primaryConstructor//获取KClass对象的private私有构造函数
        val constructor = allConstructors_2.stream().filter { it.parameters.size == 1 }.findAny().get().javaConstructor//获取与指定声明参数列表类型匹配的构造函数

    生成类的实例对象

    通过class对象和kclass对象来生成类的实例对象有两种不同的方法:

  • 使用Class对象的newInstance()方法或者Kclass对象的createInstance()方法来创建对应类的实例。这种方式要求class对象的对应类中有无参默认构造函数,而调用对应的newInstance和createInstance方法创建实例的过程实际上是利用默认构造函数来创建该类的实例:
  •     val hClass_1 = Human::class.java//获取Human类的class属性
        val human_1=hClass_1.newInstance() as Human//强制转换为Human类
    
        val hClass_2 = Human::class//获取Human类的Kclass属性
        val human_2=hClass_2.createInstance()

  • 使用class对象或者kclass对象获取指定的constructor构造函数对象,再调用该constructor构造函数对象的newInstance()方法来创建该class对象对应类的实例。通过这种方法可以选择使用指定的构造函数来创建实例:
  •     val hClass_1 = Human::class.java//获取Human类的class属性
        val constructor_1 = hClass_1.getDeclaredConstructor(String::class.java)
        val human_1=constructor_1.newInstance()
    
        val hClass_2 = Human::class//获取Human类的Kclass属性
        val allConstructors_2 = hClass_2.constructors
        val constructor = allConstructors_2.stream().filter { it.parameters.size == 1 }.findAny().get().javaConstructor
        val human_2=constructor.newInstance()

    调用类的方法

    通过class对象或者kclass对象获取指定的method方法,返回method类型的对象,调用该method对象中的invoke(Object obj,Object…args)方法来调用函数方法,第一个参数对应调用该方法的实例对象,第二个参数对应该方法的参数,可以传递多个参数。

        val hClass_1 = Human::class.java//获取Human类的class属性
        val human_1=hClass_1.newInstance() as Human
        val declareMethod_1 = hClass_1.getDeclaredMethod("say", String::class.java)
        declareMethod_1.isAccessible = true//设置private私有方法可以访问
        declareMethod_1.invoke(human_1, "Hello~!")
    
    
        val hClass_2 = Human::class//获取Human类的Kclass属性
        val human_2=hClass_2.createInstance()
        val methods_2 = hClass_2.memberFunctions//获取class对象的所有的方法,包括父类的方法
        val method_2 = methods_2.stream().filter { it.name == "say" }.findAny().get().javaMethod
        method_2!!.isAccessible = true//设置private私有方法可以访问
        method_2.invoke(human_2, "Bingo~")

    访问成员变量值

    通过class对象或者kclass获取指定的Field成员变量,返回Field类型的成员变量对象。Field提供了两组方法来读取或设置成员变量的值,通过get(Object obj)方法来获取指定obj对象实例的该成员变量的值,通过set(Object obj,XXX val)方法来设置指定obj对象实例的该成员变量的值为val值。PS:对于8种原生的基本数据类型,有相对应的优化方法,如getInt,getDouble,setInt,setDouble

        val hClass_1 = Human::class.java//获取Human类的class属性
        val human_1=hClass_1.newInstance() as Human
        val code_1: Field = hClass_1.getField("code")
        println(code_1.get(human_1)) 
        code_1.set(human_1,"0000")
    
        val hClass_2 = Human::class//获取Human类的Kclass属性
        val human_2=hClass_2.createInstance()
        val publicFields_2 = hClass_2.memberProperties
        val nameField_2 = publicFields_2.stream().filter { it.name == "name" }.findAny().get().javaField
        nameField_2!!.isAccessible = true
        nameField_2.set(human_2, "JasonChen")
        println(nameField_2.get(human_2))



版权声明:本文为qq402164452原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。