End

Kotlin 朱涛-11 注解 annotation 反射 reflect

本文地址


目录

11 | 注解与反射:进阶必备技能

注解与反射,可以提高代码的灵活性

许多著名的开源库,比如 Spring Boot、Retrofit、Gson 等,都会用到这两种技术。

注解

注解,就是对程序代码的一种补充

定义注解类

Deprecated 的定义:

@Target(CLASS, FUNCTION, PROPERTY, ANNOTATION_CLASS, CONSTRUCTOR, PROPERTY_SETTER, PROPERTY_GETTER, TYPEALIAS)
@MustBeDocumented
public annotation class Deprecated(
    val message: String, 
    val replaceWith: ReplaceWith = ReplaceWith(""),
    val level: DeprecationLevel = DeprecationLevel.WARNING // 警告程度:WARNING、ERROR、HIDDEN
)

Deprecated 的使用:

@Deprecated(
    message = "废弃了",                     // 废弃的提示信息
    replaceWith = ReplaceWith("用什么替代"), // 应该用什么来替代废弃的部分
    level = DeprecationLevel.ERROR         // IDE 会把这个问题当作是错误的来看待,所以 IDE 会直接报错
)

元注解

用来修饰其他注解的注解叫做 元注解

元注解有四个:

  • @Target使用目标,指定被修饰的注解可以用在什么地方
  • @Retention保留位置,指定被修饰的注解是不是编译后可见、是不是运行时可见
  • @Repeatable是否可重复,是否允许在同一个地方多次使用,使用场景较少
  • @MustBeDocumented生成文档,指定被修饰的注解应该包含在生成的 API 文档中显示

四个元注解的定义:

@Target(AnnotationTarget.ANNOTATION_CLASS) // Target 是注解类
@MustBeDocumented
public annotation class Target(vararg val allowedTargets: AnnotationTarget)

@Target(AnnotationTarget.ANNOTATION_CLASS)
public annotation class Retention(val value: AnnotationRetention = RUNTIME)

@Target(AnnotationTarget.ANNOTATION_CLASS)
public annotation class Repeatable

@Target(AnnotationTarget.ANNOTATION_CLASS)
public annotation class MustBeDocumented

四个元注解的 Target 都是注解类

元注解 Target 的取值

public enum class AnnotationTarget {
    CLASS,            // 类、接口、object、注解类
    ANNOTATION_CLASS, // 注解类
    TYPE_PARAMETER,   // 泛型参数
    PROPERTY,         // 属性
    FIELD,            // 字段、幕后字段
    LOCAL_VARIABLE,   // 局部变量
    VALUE_PARAMETER,  // 函数参数
    CONSTRUCTOR,      // 构造器
    FUNCTION,         // 函数
    PROPERTY_GETTER,  // 属性的 getter
    PROPERTY_SETTER,  // 属性的 setter
    TYPE,             // 类型
    EXPRESSION,       // 表达式
    FILE,             // 文件
    TYPEALIAS         // 类型别名
}

元注解 Retention 的取值

public enum class AnnotationRetention {
    SOURCE, // 源代码。注解只存在于源码中,编译后不可见
    BINARY, // 二进制。注解在编译后可见,在运行时不可见
    RUNTIME // 运行时。注解在编译后可见,在运行时也可见
}

反射

反射可以极大地提升架构的灵活性。

引入反射库

Kotlin 中的反射库并没有直接集成到标准库当中,如果要用反射,就必须要引入这个依赖:

implementation "org.jetbrains.kotlin:kotlin-reflect"

引入后,需要点击 Gradle -> Reload All Gradle Project 图标(即 Gradle 窗格左上角的同步图标)

也可以自己从 Maven 仓库中下载

案例:获取并修改程序信息

目的:读取任意对象中,所有的成员属性的名称和属性的值;并在满足指定条件时,修改程序信息。

import kotlin.reflect.KClass
import kotlin.reflect.KMutableProperty1
import kotlin.reflect.KProperty1
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible

data class Man(private var jobName: String)

fun readMembers(obj: Any) {
    val clazz: KClass<out Any> = obj::class        // 获取类引用
    val className: String? = clazz.simpleName      // 类的名称
    clazz.memberProperties.forEach {               // 遍历类的成员属性。成员属性的类型是 KProperty1
        val fieldName: String = it.name            // 属性名称
        it.getter.isAccessible = true              // 私有成员需要设置为可访问
        val fieldValue: Any? = it.getter.call(obj) // 属性值
        println("${className}.${fieldName} = $fieldValue - ${it is KMutableProperty1}")

        tryModify(obj, it)
    }
}

private fun tryModify(obj: Any, it: KProperty1<out Any, *>) {
    if (it.name == "jobName" &&                          // 判断属性的名称
        it is KMutableProperty1 &&                       // 判断属性是否可变(是否是用 var 修饰的)
        it.setter.parameters.size == 2 &&                // 判断属性的 setter 方法的参数个数
        it.getter.returnType.classifier == String::class // 判断属性的 getter 方法的返回值类型
    ) {
        it.setter.call(obj, "码农") // 调用属性的 setter 方法,参数:obj 自身 + 要设置的值
        println(obj.toString())
    }
}

fun main() {
    readMembers("白乾涛")
    readMembers(Man("程序员"))
}
String.length = 3 - false
Man.jobName = 程序员 - true
Man(jobName=码农)
  • 通过 obj::class 可以拿到类引用,也就是变量 obj 的实际类型,KClass 代表的是一个 Kotlin 类
  • 通过 KClass 可以拿到这个类型的所有信息,比如类的名称 simpleName,类的成员属性的集合 memberProperties

KMutableProperty1: Represents a var-property, operations on which take one receiver as a parameter.

反射的几个关键类

几个关键类:KClass、KCallable、KParameter、KType。

KClass

KClass 是 Kotlin 定义的一个类,和 Java 中定义的 Class 类之间没有继承关系。

val jClass: Class<String> = String::class.java // java 中定义的类所对应的 class
val kClass: KClass<String> = String::class     // kotlin 中定义的类所对应的 class

println("${kClass == jClass} - ${kClass.java == jClass}") // false  - true

KClass 代表了一个 Kotlin 的。它的重要成员有:

  • simpleName:类的名称,匿名内部类的名称为 null
  • qualifiedName:完整的类名
  • members:所有成员属性和方法,类型是 Collection<KCallable<*>>
  • constructors:类的所有构造函数,类型是 Collection<KFunction<T>>>
  • nestedClasses:类的所有嵌套类,类型是 Collection<KClass<*>>
  • visibility:类的可见性,类型是 KVisibility?,包括 PUBLIC、PROTECTED、INTERNAL、PRIVATE
  • isFinal:是不是 final
  • isOpen:是不是 open
  • isAbstract:是不是抽象的
  • isSealed:是不是密封的
  • isData:是不是数据类
  • isInner:是不是内部类
  • isCompanion:是不是伴生对象
  • isFun:是不是函数式接口
  • isValue:是不是 Value Class

KCallable

KCallable 代表了 Kotlin 当中的所有可调用的元素,比如函数、属性、构造函数。它的重要成员有:

  • name:名称,属性和函数都有名称
  • parameters:所有的参数,类型是 List<KParameter>,指的是调用这个元素所需的所有参数
  • returnType:返回值类型,类型是 KType
  • typeParameters:所有的类型参数,类型是 List<KTypeParameter>
  • call:KCallable 对应的调用方法
  • visibility:可见性
  • isSuspend:是不是挂起函数

KParameter

KParameter 代表了 KCallable 当中的参数。它的重要成员有:

  • index:参数的位置,下标从 0 开始
  • name:参数的名称,源码当中参数的名称
  • type:参数的类型,类型是 KType
  • kind:参数的种类,对应三种情况:INSTANCE(实例)、EXTENSION_RECEIVER(扩展接受者)、VALUE(实际的参数值)

KType

KType 代表了 Kotlin 当中的类型。它的重要成员有:

  • classifier:类型对应的 Kotlin 类,即 KClass
  • arguments:类型的类型参数,其实它就是这个类型的泛型参数
  • isMarkedNullable:是否在源代码中标记为可空类型,即这个类型的后面有没有?修饰

Kotlin 中的 Class

总结

Java 中获取 Class Kotlin 中获取 Class Kotlin 中获取 KClass
通过类获取 Class String.class String::class.java String::class
通过对象获取 Class obj.getClass() obj.javaClass obj::class
原始类型的 Class int.class 123.javaClass Int::class
原始类型的 Class Int::class.javaPrimitiveType xxx.kotlin
封装类型的 Class Integer.class Integer::class.java Integer::class

普通类的 Class

Class<String> clazz1 = String.class;
Class<?> clazz2 = "bqt".getClass();
val clazz1: Class<String> = String::class.java
val clazz2: Class<String> = "bqt".javaClass

val kClazz1: KClass<out String> = String::class
val kClazz2: KClass<out String> = "bqt"::class

原始类型和封装类型的 Class

Class<Integer> intClass = int.class;          // 原始类型的 Class
Class<Integer> integerClass = Integer.class;  // 封装类型的 Class

System.out.println(intClass);                 // int
System.out.println(integerClass);             // class java.lang.Integer
System.out.println(intClass == integerClass); // false。不是同一个 Class
val intClass1: Class<Int> = 123.javaClass                           // 原始类型的 Class
val intClass2: Class<Int>? = Int::class.javaPrimitiveType           // 原始类型的 Class
val integerClass: Class<Integer> = Integer::class.java              // 封装类型的 Class

val intKClass1: KClass<Int> = Int::class                            // 原始类型的 KClass
val intKClass2: KClass<Int>? = Int::class.javaPrimitiveType?.kotlin // 原始类型的 KClass
val integerKClass: KClass<Integer> = Integer::class                 // 封装类型的 KClass

数组类型的 Class

import java.lang.reflect.Array;

Class<?> saClass = Array.newInstance(String.class, 0).getClass(); // String 数组的 Class
Class<?> iaClass = Array.newInstance(int.class, 0).getClass();    // int 数组的 Class
import java.lang.reflect.Array

val sArrayClass: Class<*> = Array.newInstance(String::class.java, 0).javaClass
val intArrayClass: Class<*> = Array.newInstance(Integer::class.java, 0).javaClass

2017-10-31

posted @ 2017-10-31 17:18  白乾涛  阅读(5339)  评论(0编辑  收藏  举报