Kotlin 朱涛-11 注解 annotation 反射 reflect
目录
目录
11 | 注解与反射:进阶必备技能
注解与反射,可以提高代码的灵活性。
许多著名的开源库,比如 Spring Boot、Retrofit、Gson 等,都会用到这两种技术。
注解
注解,就是对程序代码的一种补充。
定义注解类
注解类使用 annotation class 定义。
例如,注解类 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 会直接报错
)
val name: String = "bqt"
@Deprecated(message = "方法废弃了")
fun test() = "bqt"
元注解
用来修饰其他注解的注解叫做 元注解。
元注解有四个:
- @Target:使用目标,指定被修饰的注解可以用在什么地方
- @Retention:保留位置,指定被修饰的注解是不是编译后可见、是不是运行时可见
- @Repeatable:是否可重复,是否允许在同一个地方多次使用,使用场景较少
- @MustBeDocumented:生成文档,指定被修饰的注解应该包含在生成的 API 文档中显示
四个元注解的定义
// 四个元注解只能用在注解类上,所以 Target 的值都是 ANNOTATION_CLASS
@Target(AnnotationTarget.ANNOTATION_CLASS)
@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 的取值
public enum class AnnotationTarget {
    CLASS,            // 类、接口、object、注解类
    ANNOTATION_CLASS, // 注解类,即使用【annotation class】定义的类
    TYPE_PARAMETER,   // 泛型参数
    PROPERTY,         // 属性
    PROPERTY_GETTER,  // 属性的 getter
    PROPERTY_SETTER,  // 属性的 setter
    FIELD,            // 字段、幕后字段
    LOCAL_VARIABLE,   // 局部变量
    VALUE_PARAMETER,  // 函数参数
    CONSTRUCTOR,      // 构造器
    FUNCTION,         // 函数
    TYPE,             // 类型
    EXPRESSION,       // 表达式
    FILE,             // 文件
    TYPEALIAS         // 类型别名
}
元注解 Retention 的取值
public enum class AnnotationRetention {
    SOURCE, // 源代码。注解在编译后【不可见】,在运行时也【不可见】;注解只存在于源码中
    BINARY, // 二进制。注解在编译后【可见】,在运行时【不可见】
    RUNTIME // 运行时。注解在编译后【可见】,在运行时也【可见】
}
反射
反射可以极大地提升架构的灵活性。
Kotlin 中的反射库并没有直接集成到标准库当中,如果要用反射,需要引入这个依赖:
implementation "org.jetbrains.kotlin:kotlin-reflect"
也可以自己从 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, kp: KProperty1<out Any, *>) {
    if (kp.name == "jobName" &&                          // 判断属性的名称
        kp is KMutableProperty1 &&                       // 判断属性是否可变的(即 var 修饰的)
        kp.setter.parameters.size == 2 &&                // 判断属性 setter 方法的参数个数
        kp.getter.returnType.classifier == String::class // 判断属性 getter 方法的返回值类型
    ) {
        kp.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 avar-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:类的名称,注意,匿名内部类的 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
本文来自博客园,作者:白乾涛,转载请注明原文链接:https://www.cnblogs.com/baiqiantao/p/7762573.html

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号