返回顶部
首页 > 资讯 > 移动开发 >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://lsjlt.com/news/174559.html(转载时请注明来源链接)

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

猜你喜欢
  • 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+
    2024-04-02
  • 【Java 注解】自定义注解(注解属性与使用)
    文章目录 前言一、自定义注解与元注解1.注解属性类型 二、注解的生命周期以及作用目标1.生命周期2.作用目标 三,简单使用四,注解属性赋值简化 前言 Java注解是一种元数据(m...
    99+
    2023-10-21
    java spring spring boot log4j 经验分享 笔记 后端
  • 详解Java中自定义注解的使用
    目录什么是注解注解的注意事项注解的本质自定义注解使用使用方式 1使用方式 2什么是注解 在早期的工作的时候 ,自定义注解写的比较多,可大多都只是因为 这样看起来 不会存在一堆代码耦合...
    99+
    2023-03-20
    Java自定义注解使用 Java自定义注解 Java 注解
  • 浅谈java反射和自定义注解的综合应用实例
    前言前几天学习了反射和自定义注解,刚好工作中遇到一个小问题:前台传递到后台的必填字段为空,导致不能插入数据库。就是这样一个小问题,让我考虑到是否可以做一个通用的方法,让前台传递过来的必填字段在后台也校验一遍,如果传递为空,则把响应字段返回提...
    99+
    2023-05-31
    java 自定义注解 反射
  • Golang泛型与反射的应用详解
    目录1. 泛型1.1 定义1.2 例子1.3 自定义泛型类型1.4 泛型与switch结合使用1.5 泛型实战2. 反射2.1 定义2.2 方法2.3 反射读取2.4 反射操作2.5...
    99+
    2024-04-02
  • Java注解详解及实现自定义注解的方法
    目录概念‍♀️作用⛹JDK中预定义的一些注解注解生成文档案例自定义注解格式本质属性:接口中的抽象方法元注解:用于描述注解的注解‍♂️在程序使用(解析)注解:获取注解中...
    99+
    2024-04-02
  • Java中的反射的作用以及详解
    目录 目录 1.反射是什么? 2.反射的优缺点: 3.反射的应用场景: 4.反射创建的三种方式 : 5.反射的常用方法代码示例: 1.实体类(反射获取的类): 2.反射常用方法的示例代码: 1.反射是什么? Java中的反射是指在运行...
    99+
    2023-10-24
    java 开发语言
  • Java利用自定义注解、反射实现简单BaseDao实例
    在常见的ORM框架中,大都提供了使用注解方式来实现entity与数据库的映射,这里简单地使用自定义注解与反射来生成可执行的sql语句。这是整体的目录结构,本来是为复习注解建立的项目^.^好的,首先我们来确定思路。1. 自定义@Table @...
    99+
    2023-05-31
    java 自定义注解 反射
  • Java中的注解和反射怎么使用
    这篇文章主要讲解了“Java中的注解和反射怎么使用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java中的注解和反射怎么使用”吧!1、注解(Annotation)1.1 什么是注解(Ann...
    99+
    2023-07-02
  • kotlin之协程的理解与使用详解
    前言         为什么在kotlin要使用协程呢,这好比去了重庆不吃火锅一样的道理。协程的概念并不陌生,在python也有提及。任何事务...
    99+
    2024-04-02
  • 详解Python中类的定义与使用
    类顾名思义,就是一类事物、或者叫做实例,它用来描述具有共同特征的一类事物。我们在python中声明类的关键词是class,类还有功能和属性,属性就是这类事物的特征,而功能就是它能做什么,也是就是方法或者函数...
    99+
    2022-06-04
    详解 定义 Python
  • TypeScript数组的定义与使用详解
    目录基本定义声明数组时直接初始化访问数组Array对象声明数组时指定数组大小两种定义方式声明多维数组(含泛型)普通版泛型版类数组数组解构通过接口描述数组数组迭代基本定义 声明数组时直...
    99+
    2024-04-02
  • Kotlin类与属性及构造函数的使用详解
    目录1.类的属性 filed2.构造函数3.执行顺序4.延迟初始化5.惰性初始化1.类的属性 filed 1)在kotlin中定义属性,必须赋初始值,要不编译器检查不通过。这个和ja...
    99+
    2024-04-02
  • 详解JavaSE中抽象类与接口的定义及使用
    目录一、抽象类1、抽象类定义2、抽象方法二、接口1、接口定义2、类实现接口3、接口与多态联合4、extends和implements5、接口在开发当中的作用6、is has like...
    99+
    2024-04-02
  • JAVA基础之注解与反射的使用方法和场景
    目录注解注解定义使用场景内置注解 元注解反射加载配置文件反射获取Class反射获取 Constructor反射获取 Method反射获取 Field通过反射获取注解信息内省...
    99+
    2024-04-02
  • Python自定义模块的创建与使用
    目录一、前言二、模块的概述三、自定义模块1.创建模块2.使用import语句导入模块3.使用from…import语句导入模块附:python打包用户自定义模块总结一、...
    99+
    2024-04-02
  • C++类的定义和对象的创建详解
    目录1、类的定义2、创建对象3、访问类的成员4、使用对象指针前言: 类和对象是 C++ 的重要特性,它们使得 C++ 成为面向对象的编程语言,可以用来开发中大型项目,本节重点讲解类和...
    99+
    2024-04-02
  • Java中的反射,枚举及lambda表达式的使用详解
    目录一、反射1.1定义1.2用途1.3反射基本信息1.4与反射相关的类1.5Class类(反射机制的起源)1.6Class类中的相关方法1.7获得Class对象的三种方式1.8反射的...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作