美文网首页
Kotlin中的反射

Kotlin中的反射

作者: Scott9456 | 来源:发表于2018-11-09 17:48 被阅读0次

    博客地址https://sguotao.top

    Java中的反射机制,使得我们可以在运行期获取Java类的字节码文件中的构造函数,成员变量,成员函数等信息。这一特性使得反射机制被常常用在框架中,想要比较系统的了解Kotlin中的反射,先从Java的反射说起。

    Java中的反射

    通常我们写好的.java源码文件,经过javac的编译,最终生成了.class字节码文件。这些字节码文件是与平台无关的,使用时通过Classloader去加载这些.class字节码文件,从而让程序按照我们编写好的业务逻辑运行。Java的反射主要是从这些.class文件中获取我们想要得到的内容,那么Java中的反射能够得到哪些内容呢?

    获取Class对象

    Java是面向对象的语言,同样的.class字节码文件也不例外,想要获取.class文件中的内容,就要先获取.class文件对应的Class对象。Java中获取Class对象的方式有三种。

    //1.Class.forName("类名字符串") (注意:类名字符串必须是全称,包名+类名)
    Class baseInfo = Class.forName("top.sguotao.ReflectionJava");
     //2.类名.class
    Class object = Object.class;
    //3.实例对象.getClass()
    Class date = (new Date()).getClass();
    Class testclass = this.getClass();
    

    获取类的构造函数Constructor

    获取Class对象之后,就可以获取其中的构造函数,从而去创建实例对象。类的构造函数对应java.lang.reflect.Constructor。获取构造函数归纳起来有以下五种方式:

        // 1.获取参数列表是parameterTypes,访问控制符是public的构造函数
        public Constructor getConstructor(Class[] parameterTypes)
    
        // 2.获取所有访问控制符是public的构造函数
        public Constructor[] getConstructors()
    
        // 3.获取参数列表是parameterTypes,并且是类自身声明的构造函数,访问控制符包含public、protected和private的函数。
        public Constructor getDeclaredConstructor(Class[] parameterTypes)
    
        //4.获取类自身声明的全部的构造函数,访问控制符包含public、protected和private的函数。
        public Constructor[] getDeclaredConstructors()
    
        //5.如果类声明在其它类的构造函数中,返回该类所在的构造函数,如果存在则返回,不存在返回null
        public Constructor getEnclosingConstructor()
    

    获取类的成员变量

    类的成员变量对应的是java.lang.reflect.Field,获取成员变量归纳起来有以下四种方式:

        //1.获取“名称是name”的public的成员变量(包括从基类继承的、从接口实现的所有public成员变量)
        public Field getField(String name)
    
        //2.获取全部的public成员变量(包括从基类继承的、从接口实现的所有public成员变量)
        public Field[] getFields()
    
        //3.获取“名称是name”,并且是类自身声明的成员变量,包含public、protected和private成员变量。
        public Field getDeclaredField(String name)
    
        //4.获取全部的类自身声明的成员变量,包含public、protected和private成员变量。
        public Field[] getDeclaredFields()
    

    获取类的成员函数

    类的成员函数对应的是java.lang.reflect.Method,获取成员函数归纳起来有下面5种方式:

    // 1.获取函数名是name,参数是parameterTypes的public的函数(包括从基类继承的、从接口实现的所有public函数)
        public Method getMethod(String name, Class[] parameterTypes)
    
        //2.获取全部的public的函数(包括从基类继承的、从接口实现的所有public函数)
        public Method[] getMethods()
    
        //3.获取函数名name,参数是parameterTypes,并且是类自身声明的函数,包含public、protected和private方法。
        public Method getDeclaredMethod(String name, Class[] parameterTypes)
    
        //4.获取全部的类自身声明的函数,包含public、protected和private方法。
        public Method[] getDeclaredMethods()
    
        //5.如果这个类是其它类中某个方法的内部类,调用getEnclosingMethod()就是这个类所在的方法;若不存在,返回null。
        public Method getEnclosingMethod()
    

    获取类的其它信息

    获取类的注解信息,对应的是java.lang.annotation.Annotation接口,获取类的注解信息归纳起来有下面3种方法:

    //1.获取类的annotationClass类型的注解 (包括从基类继承的、从接口实现的所有public成员变量)
    public Annotation<A> getAnnotation(Class annotationClass)
    
    //2.获取类的全部注解 (包括从基类继承的、从接口实现的所有public成员变量)
    public Annotation[] getAnnotations()
    
    //3.获取类自身声明的全部注解 (包含public、protected和private成员变量)
    public Annotation[] getDeclaredAnnotations()
    

    获取类的接口和基类的信息,对应的是java.lang.reflect.Type接口,获取类的接口和基类信息有下面两个方法:

     // 获取实现的全部接口
    public Type[] getGenericInterfaces()
    
    // 获取基类
    public Type getGenericSuperclass()
    

    获取类的其它描述信息。

        //1.获取类名
        public String getSimpleName()
    
        //2.获取完整类名
        public String getName()
    
        //3.判断类是不是枚举类
        public boolean isEnum()
    
        //4.判断obj是不是类的实例对象
        public boolean isInstance(Object obj)
    
        //5.判断类是不是接口
        public boolean isInterface()
    
        //6.判断类是不是本地类,所谓本地类,就是定义在方法内部的类。
        public boolean isLocalClass()
    
        //7.判断类是不是成员类,所谓成员类,就是常见的内部类,是指不在代码块,构造函数和成员方法中的内部类。
        public boolean isMemberClass()
    
        //8.判断类是不是基本类型。 基本类型,包括void和boolean、byte、char、short、int、long、float 和 double这几种类型。
        public boolean isPrimitive()
    

    Kotlin中的反射

    在Kotlin中,字节码对应的类是kotlin.reflect.KClass,因为Kotlin百分之百兼容Java,所以Kotlin中可以使用Java中的反射,但是由于Kotlin中字节码.class对应的是KClass类,所以如果想要使用Java中的反射,需要首先获取Class的实例,在Kotlin中可以通过以下两种方式来获取Class实例。

    //1.通过实例.javaClass
    var hello = HelloWorld()
    hello.javaClass
    
     //2.通过类Kclass类的.java属性
    HelloWorld::class.java
    

    获取了Class实例,就可以调用上面介绍的方法,获取各种在Java中定义的类的信息了。

    当然Kotlin中除了可以使用Java中的反射以外,还可以使用Kotlin中声明的一些方法,当然同Java中反射一样,想要使用这些方法,先要获取Kclass对象,在Kotlin中可以通过以下两种方式获取Kclass实例。

     //1.通过类::class的方式获取Kclass实例
    val clazz1: KClass<*> = HelloWorld::class
    //2.通过实例.javaClass.kotlin获取Kclass实例
    var hello = HelloWorld()
    val clazz2 = hello.javaClass.kotlin
    

    获取了Kclass实例之后,就可以调用Kotlin中声明的一些关于反射的方法了,那么都有哪些方法呢?

    构造函数Constructor

    Kotlin可以通过下面的方法,获取所有的构造函数。

    //返回这个类的所有构造器
    public val constructors: Collection<KFunction<T>>
    

    成员变量和成员函数

    Kotlin中获取成员变量和成员函数的方法有6个。

        //返回类可访问的所有函数和属性,包括继承自基类的,但是不包括构造器
        override val members: Collection<KCallable<*>>
        //返回类声明的所有函数
        val KClass<*>.declaredFunctions: Collection<KFunction<*>>
        //返回类的扩展函数
        val KClass<*>.declaredMemberExtensionFunctions: Collection<KFunction<*>>
        //返回类的扩展属性
        val <T : Any> KClass<T>.declaredMemberExtensionProperties: Collection<KProperty2<T, *, *>>
        //返回类自身声明的成员函数
        val KClass<*>.declaredMemberFunctions: Collection<KFunction<*>>
        //返回类自身声明的成员变量(属性)
        val <T : Any> KClass<T>.declaredMemberProperties: Collection<KProperty1<T, *>>
    

    类相关信息

    可以看到Kotlin反射中,可以获取比Java反射更多的关于类的信息。

    //1.返回类的名字
    public val simpleName: String?
     //2.返回类的全包名
    public val qualifiedName: String?
    //3.如果这个类声明为object,则返回其实例,否则返回null
    public val objectInstance: T?
    //4.返回类的可见性
    @SinceKotlin("1.1")
    public val visibility: KVisibility?
    //5.判断类是否为final类(在Kotlin中,类默认是final的,除非这个类声明为open或者abstract)
    @SinceKotlin("1.1")
    public val isFinal: Boolean
    //6.判断类是否是open的(abstract类也是open的),表示这个类可以被继承
    @SinceKotlin("1.1")
    public val isOpen: Boolean
    //7.判断类是否为抽象类
    @SinceKotlin("1.1")
    public val isAbstract: Boolean
    //8.判断类是否为密封类,密封类:用sealed修饰,其子类只能在其内部定义
    @SinceKotlin("1.1")
    public val isSealed: Boolean
    //9.判断类是否为data类
    @SinceKotlin("1.1")
    public val isData: Boolean
    //10.判断类是否为成员类
    @SinceKotlin("1.1")
    public val isInner: Boolean
    //11.判断类是否为companion object
    @SinceKotlin("1.1")
    public val isCompanion: Boolean    
    //12.返回类中定义的其他类,包括内部类(inner class声明的)和嵌套类(class声明的)
    public val nestedClasses: Collection<KClass<*>>
     //13.判断一个对象是否为此类的实例
    @SinceKotlin("1.1")
    public fun isInstance(value: Any?): Boolean
    //14.返回这个类的泛型列表
    @SinceKotlin("1.1")
    public val typeParameters: List<KTypeParameter>
    //15.类其直接基类的列表
    @SinceKotlin("1.1")
    public val supertypes: List<KType>
    //16.返回类所有的基类
    val KClass<*>.allSuperclasses: Collection<KClass<*>>
    //17.返回类的伴生对象companionObject
    val KClass<*>.companionObject: KClass<*>?
    

    使用Kotin中反射注意的问题

    在Kotlin1.1中如果反射String,Map,List等类型时,会抛出一个built-in Kotlin Types的异常,这是因为在Kotlin1.1版本中还没有对这些类型添加支持,在Kotlin1.2版本中,这个问题已经解决。

    Kotlin关于反射的内容都放在kotlin-reflect的jar包中,这个jar包有2.6M,对于移动端开发,需要占用一定的内存空间。

    最后就是关于Kotlin反射的效率问题,在Java中反射大概需要几十微秒,在Kotlin就需要几百甚至上千微秒,如果是通过反射访问对象或构造属性,甚至需要上万微秒,对此,官方给出的解释是,现在还没有精力进行优化,相信后续的版本中,效率问题会有所改善。

    参考链接

    1. Kotlin Bootcamp for Programmers
    2. Kotlin Koans

    相关文章

      网友评论

          本文标题:Kotlin中的反射

          本文链接:https://www.haomeiwen.com/subject/pnwoxqtx.html