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 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:类的名称,匿名内部类的名称为
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