广告
返回顶部
首页 > 资讯 > 移动开发 >Kotlin注解与反射的定义及创建使用详解
  • 657
分享到

Kotlin注解与反射的定义及创建使用详解

Kotlin注解反射创建使用Kotlin注解反射 2022-12-08 20:12:59 657人浏览 安东尼
摘要

目录1.注解1.定义2.注解的创建3.注解的使用2.反射1.定义2.反射的应用1.注解 1.定义 注解是将元数据附加到代码的地方。从字面意思理解它就是对知识点的补充,一种描述。在J

1.注解

1.定义

注解是将元数据附加到代码的地方。从字面意思理解它就是对知识点的补充,一种描述。在Java中最常见的注解就是@Override或者就是Retrofit中的@GET@POST等。

2.注解的创建

创建时用annotation修饰符进行声明,如

annotation class GET()

这样就创建好了一个注解,但是这里想要完全使用还要添加一些属性:

  • @Target:指定可以用该注解标注的元素的可能的类型(类、函数、属性、表达式);
  • @Retention:指定该注解是否存储在编译后的class文件中以及它在运行时能否通过反射可见,默认为true;
  • @Repeatable:允许在单个元素上多次使用相同的该注解;
  • @MustBeDocumented:指定该注解是公有api的一部分,并且应该包含在生成的API文档中显示的类或方法的签名中,一般用于SDK文档中。

这里重点要注意的是 @Target和@Retention

//@Target
public enum class AnnotationTarget {
    // 类、接口、object、注解类
    CLASS,
    // 注解类
    ANNOTATION_CLASS,
    // 泛型参数
    TYPE_PARAMETER,
    // 属性
    PROPERTY,
    // 字段、幕后字段
    FIELD,
    // 局部变量
    LOCAL_VARIABLE,
    // 函数参数
    VALUE_PARAMETER,
    // 构造器
    CONSTRUCTOR,
    // 函数
    FUNCTION,
    // 属性的getter
    PROPERTY_GETTER,
    // 属性的setter
    PROPERTY_SETTER,
    // 类型
    TYPE,
    // 表达式
    EXPRESSION,
    // 文件
    FILE,
    // 类型别名
    TYPEALIAS
}
//@Retention
public enum class AnnotationRetention {
    // 注解只存在于源代码,编译后不可见
    SOURCE,
    // 注解编译后可见,运行时不可见
    BINARY,
    // 编译后可见,运行时可见	默认
    RUNTIME
}

3.注解的使用

@Target(AnnotationTarget.FUNCTION)			//注解用于方法
@Retention(AnnotationRetention.RUNTIME)		//运行时可见,编译时可见
annotation class Custom()
//正常使用不报错
@Custom
fun test() {
    println("")
}
//报错,因为注解不支持Class,如果要支持就需要在@Target里面加上AnnotationTarget.CLASS
@Custom
class Test{
}

上面的代码是一个自定义且最简单的一个用法,现在看一下Kotlin中自带的一个注解,这个注解用来标注废弃的方法或者类等定义,比较常见

@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
)

@Tageget:支持类、 函数、 属性、注解类、构造器、属性 getter、属性 setter、类型别名

Deprecated内部还传递了几个参数:

  • message:对废弃内容的提示信息
  • repleaceWith:表示用什么内容来替代被废弃的内容。需要注意的是后面的ReplaceWith也是一个注解,也就是说Kotlin中注解中是可以添加注解的,只不过添加时不可以用@
  • level:警告程度,有WARNINGERRORHIDDEN

注解中允许的参数有:

  • 对应于 Java 原生类型的类型(Int、 Long等)
  • 字符串
  • 类(Foo::class)
  • 枚举
  • 其他注解
  • 上面已列类型的数组

注解参数不能有可空类型,因为 JVM 不支持将 null 作为注解属性的值存储。

2.反射

1.定义

反射是指计算机程序在运行时(runtime)可以访问、检测和修改它本身状态或行为的一种能力。用比喻来说,反射就是程序在运行的时候能够“观察”并且修改自己的行为。

反射在业务开发中用的较少,主要是在架构设计中,可以极大地提升架构的灵活性。

Kotlin的反射具备三个特点:

  • 感知程序的状态,包含程序的运行状态以及源代码结构;
  • 修改程序的状态;
  • 根据程序的状态,调整自身的决策行为。

2.反射的应用

首先要加入一个依赖才可以使用反射

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

然后根据上面提到的三个特点进行讲解:

  • 感知程序的状态:

举例:假设现在有两个对象,在不传递具体对象的前提下想要打印出他们每一个属性的名称以及具体的值

fun main() {
    val person = Person("张三", 22)
    val animal = Animal("猫", "用脚走", "猫粮")
    findClassAttribute(person)
    findClassAttribute(animal)
}
fun findClassAttribute(obj: Any) {
}
data class Person(val name: String, var age: Int)
data class Animal(var species: String, val walkWay: String, var food: String)
//期望结果:
//Person.name = 张三
//Person.age = 22
//Animal.species = 猫
//Animal.walkWay = 用脚走
//Animal.food = 猫粮

上面只是定义了两个类,具体项目中可能会很有很多的类,因此用when的方式是行不通的因为这样工作量还是比较大的,那么用反射反而是一个比较好的方式,那要如何实现?

fun findClassAttribute(obj: Any) {
    obj::class.memberProperties.forEach {
        println("${obj::class.simpleName}.${it.name} = ${it.getter.call(obj)}")
    }
}
//输出结果
//Person.age = 22
//Person.name = 张三
//Animal.food = 猫粮
//Animal.species = 猫
//Animal.walkWay = 用脚走

看到这个是不是一脸懵?这是啥鬼东西。我们对上面的代码进行分析就明白了:

  • obj::class: 这是类引用,是Kotlin的反射语法,通过这样的语法可以获取变量的类型信息并且可以拿到这个变量的类型KClass,也就是我们的PersonAnimal
  • memberProperties: 通过前面的obj::class拿到了具体的类型,那么也就拿到了这个这个类型的所有信息,比如说simpleNameconstructors,而memberProperties就是获取类的成员属性,然后通过foreach遍历出来就好了。
  • it:KProperty1: 这里的KProperty1是KClass的子类,通过it.name拿到属性的命名,it.getter.call拿到属性的值。

经过上述几个关键信息就获取到了我们想要的输出结果,这就是感知程序的状态。

  • 修改程序状态

拿到每个属性的命名和值之后我想要修改动物类的某个属性的值怎么办?增加一个changeClassAttributeValue方法用来修改属性值

fun changeClassAttributeValue(obj: Any) {
    obj::class.memberProperties.forEach {
        if (it.name == "food"                                   //判断要修改的属性名是【food】
            && it is KMutableProperty1                          //判断这个属性是否可以被修改,val属性不可被修改,var属性可以
            && it.setter.parameters.size == 2                   //修改属性需要setter,我们要先判断 setter 的参数是否符合预期,这里 setter 的参数个数应该是 2,第一个参数是 obj 自身,第二个是实际的值
            && it.getter.returnType.classifier == String::class //判断要修改的属性是不是string类型
        ) {
            it.setter.call(obj, "鸡胸肉")						 //修改属性值
            println("========属性值修改========")
        }
    }
}
fun main() {
    val person = Person("张三", 22)
    val animal = Animal("猫", "用脚走", "猫粮")
    changeClassAttributeValue(animal)
    findClassAttribute(animal)
}
//输出结果
//Animal.food = 鸡胸肉
//Animal.species = 猫
//Animal.walkWay = 用脚走

根据属性值food猫粮修改为鸡胸肉。 这种操作方式就是反射的第二个特点:修改程序的状态

  • 根据程序的状态,调整自身的决策行为。

上面我们已经调整状态了,那么我还想加一个修改属性:species,吃鸡胸肉的也可以是小狗,因此我们只需要再加一个else即可,这样就实现了最后一个特点:根据程序的状态,调整自身的决策行为。

fun changeClassAttributeValue(obj: Any) {
    obj::class.memberProperties.forEach {
        if (it.name == "food"                                   //判断要修改的属性名是【food】
            && it is KMutableProperty1                          //判断这个属性是否可以被修改
            && it.setter.parameters.size == 2                   //修改属性需要setter,我们要先判断 setter 的参数是否符合预期,这里 setter 的参数个数应该是 2,第一个参数是 obj 自身,第二个是实际的值
            && it.getter.returnType.classifier == String::class //判断要修改的属性是不是string类型
        ) {
            it.setter.call(obj, "鸡胸肉")
            println("======== food 属性值修改========")
        } else if (it.name == "species"                         //判断要修改的属性名是【species】
            && it is KMutableProperty1                          //判断这个属性是否可以被修改,val属性不可被修改,var属性可以
            && it.setter.parameters.size == 2                   //修改属性需要setter,我们要先判断 setter 的参数是否符合预期,这里 setter 的参数个数应该是 2,第一个参数是 obj 自身,第二个是实际的值
            && it.getter.returnType.classifier == String::class //判断要修改的属性是不是string类型
        ) {
            it.setter.call(obj, "小狗")
            println("======== species 属性值修改========")
        } else { // 差别在这里
            println("没找到相关属性")
        }
    }
}
fun main() {
    val person = Person("张三", 22)
    val animal = Animal("猫", "用脚走", "猫粮")
    changeClassAttributeValue(animal)
    findClassAttribute(animal)
}
//输出结果
//Animal.food = 鸡胸肉
//Animal.species = 小狗
//Animal.walkWay = 用脚走

这里还要说明的是可修改的属性值一定是用var修饰的,如果在demo过程中出现不能修改的要检查属性声的明是否可修改。

上面的代码通过memberProperties进入之后可以发现它用到了 Kotlin 反射的几个关键类:KClass、KCallable、KParameter、KType。现在,我们来进一步看看它们的关键成员

KClass 代表了一个 Kotlin 的类,下面是它的重要成员:

  • simpleName: 获取类名称,如果是匿名内部类获取的值为null;
  • qualifiedName: 完整的类名。用【.】分隔
  • members: 可访问的所有函数和属性,类型为Collection<KCallable<*>>
  • constructors: 获取所有构造函数,类型为Collection<KFunction<T>>
  • nestedClasses: 获取声明的所有类包含内部嵌套类和嵌套静态类,类型为Collection<KClass<*>>
  • objectInstance: 对象声明的实例如果这个类不是对象声明则返回null;
  • typeParameters: 获取参数类型列表,但不包括外部类的参数类型,类型为List<KTypeParameter>
  • supertypes: 该类的直接超类型列表,按它们在源代码中列出的顺序排列,类型为List<KType>
  • sealedSubclasses: 如果是密封类则直接获取子类的列表否则为空,类型为List<KClass<out T>>
  • visibility: 类的可见性,如果可见性不能在Kotlin中表示则为null;
  • isFinal: 返回该类是否是final类型,类型为Boolean,如果是则为true;
  • isOpen: 返回该类是否是open修饰的,类型为Boolean,如果是则为true;
  • isAbstract: 返回该类是否是abstract的,类型为Boolean,如果是则为true;
  • isSealed: 返回该类是否是密封类,类型为Boolean,如果是则为true;
  • isData: 返回该类是否是数据类,类型为Boolean,如果是则为true;
  • isInner: 返回该类是否是内部类,类型为Boolean,如果是则为true;
  • isCompanion: 返回该类是否是伴生对象,类型为Boolean,如果是则为true;
  • isFun: 返回该类是否是函数式接口,类型为Boolean,如果是则为true;
  • isValue: 返回该类是否是Value Class,类型为Boolean,如果是则为true。

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

  • name: 获取可调用的声明的名称,如果可调用对象没有名称则创建一个特殊的名称,没有名称包括:

构造函数名称为"";

属性访问器:一个名为foo的属性getter将会有名称,同理setter将会有名称

  • parameters 获取所有可调用的参数,如果需要this实例或者扩展接收方参数那么他们通过List返回,返回类型为List<KParameter>
  • returnType 获取返回值的类型,返回类型为KType
  • typeParameters 获取可调用参数的类型以列表返回,返回类型为List<KTypeParameter>
  • visibility 元素的可见性,如果可见性不能在Kotlin中表示则为null;
  • isFinal 返回该元素是否是final修饰的,类型为Boolean,如果是则为true;
  • isOpen 返回该元素是否是open修饰的,类型为Boolean,如果是则为true;
  • isAbstract 返回该元素是否是abstract修饰的,类型为Boolean,如果是则为true;
  • isSuspend 返回该元素是否是挂起函数,类型为Boolean,如果是则为true。

KParameter,代表了KCallable当中的参数,它的重要成员如下:

  • index: 参数在参数列表中的索引,从0开始;
  • name: 参数声明的名称,如果没有名称或者名称在运行时不可用则返回null;
  • type: 参数类型,对于可变参数参数类型是数组而不是单个元素,返回类型为KType
  • kind: 参数的种类

INSTANCE: 对象的实例;

EXTENSION_RECEIVER: 扩展接收者;

VALUE: 具体的值;

  • isOptional: 如果此参数是可选的,则为true,当通过KCallable进行调用时可以省略此参数。callBy,否则为false。参数可选的情况如下:

默认值在该参数的声明中提供;

形参在成员函数中声明,并且在超函数中有一个对应的形参是可选的。

  • isVararg: 如果参数为可变长度则返回true。

KType,代表了 Kotlin 当中的类型,它重要的成员如下:

  • classifier: 类型对应Kotlin类,即KClass,如果不能在Kotlin中表示则返回null;
  • arguments: 是该类型中的分类器的参数传递的类型参数,就是泛型。
  • isMarkedNullable: 是否被标记为可空类型,就是后面有没有【?】。

这几个类集合了很多个API,了解每一个的作用之后再了解反射就会很简单了。

以上就是Kotlin注解与反射的定义及使用详解的详细内容,更多关于Kotlin注解反射的资料请关注编程网其它相关文章!

--结束END--

本文标题: Kotlin注解与反射的定义及创建使用详解

本文链接: https://www.lsjlt.com/news/174559.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Kotlin注解与反射的定义及创建使用详解
    目录1.注解1.定义2.注解的创建3.注解的使用2.反射1.定义2.反射的应用1.注解 1.定义 注解是将元数据附加到代码的地方。从字面意思理解它就是对知识点的补充,一种描述。在J...
    99+
    2022-12-08
    Kotlin注解反射创建使用 Kotlin注解反射
  • Java反射的定义和用法详解
    目录定义获取Class对象1.1 通过类名获取Class对象1.2 通过对象获取Class对象1.3 通过类字面常量获取Class对象获取类的信息2.1 获取所有字段2.2 获取指定...
    99+
    2023-05-18
    Java反射 Java反射操作
  • 详解Java拦截器以及自定义注解的使用
    目录1,设置预处理,设置不需要拦截的请求2.UserTokenInterceptor ,securityInterceptor分别处理不同的请求拦截,执行不同的拦截逻辑。3.关于注解...
    99+
    2022-11-12
  • 详解Java中自定义注解的使用
    目录什么是注解注解的注意事项注解的本质自定义注解使用使用方式 1使用方式 2什么是注解 在早期的工作的时候 ,自定义注解写的比较多,可大多都只是因为 这样看起来 不会存在一堆代码耦合...
    99+
    2023-03-20
    Java自定义注解使用 Java自定义注解 Java 注解
  • 浅谈java反射和自定义注解的综合应用实例
    前言前几天学习了反射和自定义注解,刚好工作中遇到一个小问题:前台传递到后台的必填字段为空,导致不能插入数据库。就是这样一个小问题,让我考虑到是否可以做一个通用的方法,让前台传递过来的必填字段在后台也校验一遍,如果传递为空,则把响应字段返回提...
    99+
    2023-05-31
    java 自定义注解 反射
  • Kotlin类与属性及构造函数的使用详解
    目录1.类的属性 filed2.构造函数3.执行顺序4.延迟初始化5.惰性初始化1.类的属性 filed 1)在kotlin中定义属性,必须赋初始值,要不编译器检查不通过。这个和ja...
    99+
    2022-11-13
  • 详解JavaSE中抽象类与接口的定义及使用
    目录一、抽象类1、抽象类定义2、抽象方法二、接口1、接口定义2、类实现接口3、接口与多态联合4、extends和implements5、接口在开发当中的作用6、is has like...
    99+
    2022-11-13
  • 详解Python中类的定义与使用
    类顾名思义,就是一类事物、或者叫做实例,它用来描述具有共同特征的一类事物。我们在python中声明类的关键词是class,类还有功能和属性,属性就是这类事物的特征,而功能就是它能做什么,也是就是方法或者函数...
    99+
    2022-06-04
    详解 定义 Python
  • TypeScript数组的定义与使用详解
    目录基本定义声明数组时直接初始化访问数组Array对象声明数组时指定数组大小两种定义方式声明多维数组(含泛型)普通版泛型版类数组数组解构通过接口描述数组数组迭代基本定义 声明数组时直...
    99+
    2022-11-13
  • JAVA基础之注解与反射的使用方法和场景
    目录注解注解定义使用场景内置注解 元注解反射加载配置文件反射获取Class反射获取 Constructor反射获取 Method反射获取 Field通过反射获取注解信息内省...
    99+
    2022-11-11
  • Java中数组的定义与使用详解
    目录数组的基本概念数组引用传递数组静态初始化二维数组总结数组的基本概念 如果说现在要求你定义100个整型变量,那么如果按照之前的做法,可能现在定义的的结构如下: int i1, ...
    99+
    2022-11-12
  • Java中的反射,枚举及lambda表达式的使用详解
    目录一、反射1.1定义1.2用途1.3反射基本信息1.4与反射相关的类1.5Class类(反射机制的起源)1.6Class类中的相关方法1.7获得Class对象的三种方式1.8反射的...
    99+
    2022-11-13
  • 详解Java方法method的定义与调用及重载
    目录方法的定义和调用什么是方法方法的声明格式方法的调用方式方法的详细说明总结方法的重载什么是方法重载构成方法重载的条件总结方法的定义和调用 什么是方法 方法(method)就是一段用...
    99+
    2022-11-13
  • JavaScript函数防抖与函数节流的定义及使用详解
    目录一、函数防抖(Debouncing)1、基本概念2、算法思想3、代码实现4、使用场景二、函数节流(Throlle)1、基本概念2、算法思想3、代码实现4、使用场景一、函数防抖(D...
    99+
    2022-11-13
    JavaScript函数防抖 JavaScript函数节流 JavaScript 函数防抖 节流
  • Java详解数据类型的定义与使用
    目录标识符和关键字标识符什么是标识符标识符的定义规则关键字常量和变量常量变量变量的声明格式变量的声明基本数据类型整数类型浮点类型浮点类型常量浮点类型变量字符类型字符型字符串型布尔类型...
    99+
    2022-11-13
  • 【Python详解】Python类的详细定义与使用案例
    大家好,我是洲洲,欢迎关注,一个爱听周杰伦的程序员。关注公众号【程序员洲洲】即可获得10G学习资料、面试笔记、大厂独家学习体系路线等…还可以加入技术交流群欢迎大家在CSDN后台私信我! 本文目...
    99+
    2023-09-04
    python python类 类的定义 python类的使用 python类的定义
  • Java详解表格的创建与使用流程
    目录Java 的表格JTable的构造函数表格的创建 小结Java 的表格 表格是一个由多行,多列组成的二维显示区。Swing的JTable以及相关类提供了对这种表格的支持...
    99+
    2022-11-13
  • git hooks的作用及创建使用示例详解
    目录1. 什么是git hooks ?有什么用啊?1.1 是个啥1.2 有啥用?我以前没用过它不也一样好好的吗?我干嘛要用它?2. 怎么创建一个 git hooks ?2.1 首先看...
    99+
    2022-11-13
  • Python学习之函数的定义与使用详解
    目录函数的定义函数的分类函数的创建方法-def函数的返回值-returnreturn与print的区别函数的传参必传参数默认参数不确定参数(可变参数)参数规则函数小练习函数的参数类型...
    99+
    2022-11-13
  • 详解C++中函数模板的定义与使用
    目录1. 前言2. 初识函数模板2.1 语法2.2 实例化2.3 实参推导3. 重载函数模板1. 前言 什么是函数模板? 理解什么是函数模板,须先搞清楚为什么需要函数模板。 如果现在...
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作