【Kotlin学习】Kotlin中的注解

声明并应用注解

应用注解

在kotlin中使用注解的方法和java一样,以@字符作为注解名字的前缀,并放在要注解的声明的最前面

使用JUnit框架可以用@Test标记一个测试方法

image.png
注意!在AndroidStudio中想要使用该注解需要到对应的test目录中才能标记成功

image.png

@Deprecated注解

它的含义和java中一样,kotlin用replaceWith参数增强了它,让你可以提供一个替代者的(匹配)模式,以支持平滑地过渡到API新版本

image.png 实参在括号中传递,就和常规函数的调用一样。用了这种声明后,若有人使用这个函数,IDEA会提示用哪个函数代替它,并提供一个快速修正

注解只能拥有如下类型的参数:基本数据类型、字符串、枚举、类引用、其他的注解类,以及前面这些类型的数组

把一个类指定为注解实参
在类名后加上::class

把另一个注解指定为一个实参
去掉注解名称前面的@ 比如前面的ReplaceWith是一个注解,当你把它指定为Deprecated注解的实参时没有用到@

把一个数组指定为一个实参
使用arrayOf参数:@RequestMapping(path=arrayOf("/foo","/bar"))。如果注解类是在java中声明的,命名为value的形参按需自动转换成可变长度的形参

注解实参需要在编译器就是已知的,所以不能引用任意的属性作为实参。要把属性当作注解实参使用,需要用const修饰符标记它。用const标注的属性可以声明在一个文件的顶层或者一个object中,而且必须初始化为基本数据类型或者String类型的值

注解目标

许多情况下kotlin源代码中的单个声明会对应多个java声明,且每个都能携带注解

一个kotlin属性对应一个java字段、一个getter、一个潜在的setter和它的参数。而一个在主构造方法的属性还多拥有一个对应的构造方法的参数

使用点目标声明被用来说明要注解的元素。使用点目标被放在@符号和注解名称之间,并用冒号和注解名称隔开。例: @get:Rule

在JUnit中可以指定一个每个测试方法被执行之前都会执行的规则。如标准的**TemporaryFolder规则用来创建文件和文件夹并在测试结束后删除它们**

要指定一个规则,在java中需要声明一个用@Rule注解的public字段或方法。如果在你的测试类中只是用@Rule注解了属性folder,你会得到一个异常,因为@Rule被应用到了字段上,而字段默认是私有的要把它应用到(公有的)getter上,要显式地写出来,@get:Rule image.png

如果你使用java中声明的注解来注解一个属性,它会被默认应用到相应的字段上,kotlin也可以让你声明被直接对应到属性上的注解

kotlin支持的使用点目标

使用点目标含义
propertyjava的注解不能应用这种使用点目标
field为属性生成的字段
get属性的getter
set属性的setter
receiver扩展函数或者扩展属性的接收者参数
param构造方法的参数
setparam属性setter的参数
delegate为委托属性存储委托实例的字段
file包含在文件中声明的顶层函数和属性的类

任何应用到file目标的注解都必须放在文件的顶层,放在package指令之前@JvmName是常见的应用到文件的注解之一,它改变了对应类的名称。和java不一样的是kotlin允许你对任意的表达式应用注解,而不仅仅是类和函数的声明及类型。最常见的是@Suppress注解,可以用它抑制被注解的表达式的上下文中特定的编译器警告

用注解控制Java API

注解@Volatile@Strictfp直接充当了java关键字volatilestrictfp
其他注解则是被用来改变kotlin声明对java调用者的可见性

@JvmName:改变由kotlin生成的java方法或字段的名称
@JvmStatic用在对象声明或者伴生对象的方法上,把它们暴露成java的静态方法
@JvmOverload指导kotlin编译器为带默认参数值的函数生成多个重载函数
@JvmField可以应用于一个属性,把这个属性暴露成一个没有访问器的公有java字段

使用注解定制JSON序列化

序列化是一个过程,把对象转换成可以存储或者在网络上传输的二进制或者文本的表示法,反序列化则是把这种表示法转换回一个对象。最常见的一种用来序列化的格式就是JSON,包括Jackson和GSON。现在我们使用JKid库进行序列化相关操作

image.png

image.png
一个对象的JSON表示法由键值对组成:具体实例的属性名称和他们值之间的键值对

从JSON表示法中取回一个对象要调用deserialize函数

image.png
当你从JSON数据中创建实例的时候,必须显式地指定一个类作为类型参数,因为JSON没有存储对象的类型,上图要传递Person类

注意序列化之后的类能包含的不仅是这些基本类型数据或String类型的值,还可以是集合以及其他值对象类的实例

image.png

你可以使用注解来定制对象序列化和反序列化的方式。当把一个对象序列化成JSON的时候,默认情况下这个库尝试序列化所有属性,并使用属性名称作为键。注解允许你改变默认的行为

@JsonExclude

用来标记一个属性,这个属性应该排除在序列化和反序列化之外

@JsonName

让你说明代表这个属性的(JSON)键值对之中的键应该是一个给定的字符串而不是属性的名称

image.png
这里注解了name属性,改变了在JSON中用来表示它的键,age在序列化操作时会排除它

image.png

声明注解

以JKid库的注解为例

image.png
在class前加上了annotation修饰符

注解类只是用来定义关联到声明和表达式的元数据的结构。因此编译器禁止为一个注解类指定类实体

对拥有参数的直接来说,在类的主构造方法中声明这些参数

image.png
对一个注解类的所有参数来说,val关键字是强制的

在java中

image.png
java注解中有一个叫作value的方法,而kotlin注解有一个name属性。java的value方法很特殊:当你应用一个注解时,你要提供value以外所有指定特性的显式名称。而kotlin中应用注解就是常规的构造方法调用。可以使用命名实参语法让实参的名称变为显式的。如果你要把java中声明的注解应用到kotlin元素上,必须对除了value以外的所有实参使用命名实参语法,而value会被kotlin特殊对待

元注解:控制如何处理一个注解

和java一样,一个kotlin注解类自己也可以被注解。元注解是可以应用到注解类上的注解。许多依赖注入库使用了元注解来标记其他注解,表示这些注解用来识别拥有同样类型的不同的可注入对象。

标准库中定义的元注解最常见的就是@Target。上面的JsonName和JsonExclude的声明使用它为这些注解指定有效的目标

image.png
@Target元注解说明了注解可以被应用的元素类型。如果不使用它,所有的声明都可以应用这个注解,而它只需要处理属性的注解

AnnotationTarget枚举的值列出了可以应用注解的全部可能的目标,包括:类、文件、属性、属性访问器、所有的表达式等等。如果需要还可以声明多个目标

要声明自己的元注解,使用ANNOTATION_CLASS作为目标

image.png

注意!在java代码中无法使用目标为PROPERTY的注解,要让这样的注解可在java使用,可以给它添加第二个目标AnnotationTarget.FIELD

@Retention注解

它被用来说明你声明的注解是否会存储到.class文件,以及在运行时是否可以通过反射来访问它。java默认会在.class文件中保留注解但不会让它们在运行时被访问到。大多数注解确实需要在运行时存在,所以kotlin中注解拥有RUNTIME期,因此JKid中的注解没有显式地指定保留期

使用类做注解参数

在JKid库中,这出现在@DeserializeInterface注解中,它允许你控制那些接口类型属性的反序列化。不能直接创建一个接口的实例,因此需要指定反序列化时哪个类作为实现被创建

image.png

image.png

image.png

当JKid读到一个Person类实例嵌套的company对象时,它创建并反序列化了一个CompanyImpl的实例,把它存储在company属性中。使用CompanyImpl::class作为@DeserializeInterface注解的实参来说明这一点。通常使用类名称后面跟上::class关键字来引用一个类

KClass是java的java.lang.Class类型在kotlin中的对应类型。它用来保存kotlin类的引用。KClass的类型参数说明了这个引用可以指向哪些kotlin类,例如CompanyImpl::class的类型是KClass<CompanyImpl>,它是这个注解形参类型(<out Any>)的子类型

使用泛型类做注解参数

默认情况下,JKid把非基本数据类型的属性当成嵌套的对象序列化,但你可以改变这种行为并为某些值提供你自己的序列化逻辑 @CustomSerializer注解接受一个自定义序列化器类的引用作为实参。这个序列化器应该实现ValueSerializer接口

image.png

@CustomSerializer注解声明

image.png
ValueSerializer类是泛型的而且定义了一个类型形参,所以在你引用该类型的时候需要提供一个类型实参值,因为你不知道任何关于那些应用了这个注解的属性类型的信息

KClass<out ValueSerializer<*>>out表明接收任何实现了ValueSerializer的接口,不只是ValueSerializer::class,*表明允许ValueSerializer序列化任何值

文末

我总结了一些Android核心知识点,以及一些最新的大厂面试题、知识脑图和视频资料解析。

需要的小伙伴直接点击文末小卡片免费领取哦,以后的路也希望我们能一起走下去。(谢谢大家一直以来的支持,需要的自己领取)

Android学习PDF+架构视频+面试文档+源码笔记

部分资料一览:

  • 330页PDF Android学习核心笔记(内含8大板块)

  • Android学习的系统对应视频

  • Android进阶的系统对应学习资料

  • Android BAT大厂面试题(有解析)

领取地址:

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值