欢迎光临
我们一直在努力

kotlin中对注解,元注解的探讨

上篇讲了反射涉及到“元”这个概念,那随之而来的就是注解了,关于这方面知识在学习的时候,也是翻了不少文章,都写的特别高级导致我一点都看不懂,历经几天时间,现在大概能懂点皮毛吧,我来随便扯几句

其实注解也是一种非常典型的元数据,你想想:“描述数据的数据”。它不改变数据本身,只是给数据附加一些说明信息,这就是注解

我现在先写一个函数

然后在java中调用它

正常输出两次“hello java”,在调用这个 greet 函数时,必须把两个参数填满,少一个都不行

调用就比较的麻烦,这时给函数加一个注解@JvmOverloads

然后再在java里调用一下

加上这个注解之后,编译器会在底层“强行”多造出几个重载方法。在 Java 里就可以像调用 Java 原生重载方法一样,随心所欲地省略参数了

会发现不需要传全部的参数,没传的参数就直接用形参了

但是有个注意点,如果有的参数没有默认值,那么对于有默认值的参数尽量放在右边

因为这个注解是从右向左看的(@JvmOverloads 是从右往左剥洋葱的。),如果你这么写

这样写greet("java")也不会成功,所以带参数的都要写在后面

下面一个是@JvmName注解,相当于可以给kotlin文件和函数进行重命名操作(java调用的时候)

写一个函数(文件名就是Jvmname)

这种情况下,在java里正常要怎么调用呢

但是这么写肯定是麻烦的,所以用注解给他重命名一下

这样在调用的时候就可以MathUtils.and()了

接下来讲@JvmStatic,在这之前,讲一下类的伴生对象,先写一个类带一个成员函数

然后如果要调用它,需要创建实例再调用

但是如果在类里放一个对象object,

写在类里的 fun 是"实例成员",必须有对象才能调;写在 object 里的是"类级别成员",不用创建实例就能调

如果想直接outer.hello(),那就把对象设置为伴生对象

就可以直接Outer2.hello()来调用了。

然后来讲这个@JvmStatic注解,注意:它只能用在 companion object 或 object 里的成员上,普通类的普通方法上用它没意义/会报错

然后在java里调用它,就可以直接调用

Kotlin 的 companion object 在 JVM 里其实是个叫 Companion 的内部类对象
如果不加注解,Java 得这么调:Utility.Companion.xxx()
加了 @JvmStatic 后,编译器会在 Utility 这个类上直接生成一个真正的 static 方法,Java 就能直接 Utility.xxx() 了

接下来是@Deprecated,标记过时

他有一些参数,需要在注解的时候注释,怎么写取决于你期望怎么用,
Kotlin 官方 @Deprecated 注解的源码定义:

第一个message是必填的废弃提示信息,第二个是替换方案,第三个应该就是提示的级别

这里我写一个需要替换旧函数到新函数的注解

说一下,注解修饰的是紧跟在它后面的那一个代码元素(不管中间空了多少个空行)

然后开始写函数定义

按照这样的话,如果调用oldFun函数,他就会警告(ERROR),然后提示“换一个新的函数”,然后提供newFun解决方案(replaceWith)

测试一下

没问题,也可以换一个提示level,WARNING:

接下来就是@Throws注解

先看一下官方源码,看看使用这个注解需要写啥

看到KClass就要想到之前讲的了。所以里面要写上异常类的引用(反射获取)

异常就分为很多了,IOException(输入输出异常)、IllegalArgumentException(参数不合法异常)、IllegalStateException(状态不对异常)

现在在kotlin里写两个顶层函数,一个加上注解,一个不加,然后java里对其调用

然后java里对其调用

这里为什么我调用的时候加了参数还会报错呢?Java 编译器是个“死脑筋”,它只看签名,不看你的参数!

Kotlin 方法签名上带了 @Throws(IOException::class),说明只要调用这个方法,就有可能抛出 IOException。

那java看到了,不管你是不是传了空字符串,总之你必须写 try-catch 或者在方法头写 throws,否则不让过

可以看到,对于加注解的报错了,看看是报了什么

提示我们需要处理一下这个IOException,那怎么处理呢,点开more actions

它会提示两种处理方法,一种是上面加个throws IOException,一个是加try-catch。

我写trry-catch,这个异常捕获机制怎么写:

就照这么写

那如果kotlin里写的不是顶层函数,java咋调用

那肯定不能直接函数名调了

要创建实例才可以合法调用,

那try-catch里怎么写

可以的,这就是throw,把错误提前到 Java 的编译阶段就能被发现,然后对其进行处理

接下来是@Suppress(),他可以把黄色警告给藏起来。比如这里写一个“危险”的函数

因为value是any,所以会警告,那我如何给她隐藏起来呢

看一下这个警告的说明,

直接捕捉到关键字样,那就可以确定注解里填什么了

这些应该是一些基本的能用到的注解了。

上面讲了注解是一种元数据,那么有没有比注解再高一层的,元注解这种东西呢?

那肯定有的

顾名思义,元注解就是描述注解的注解,在这之前,先学习一下自定义注解,

元注解是自定义注解的组成要素

先自定义个最简单的注解,标记注解

主要是看这个定义的方法,这个形式要记一下

定义后就可以直接用了,像之前讲的注解的用法一样

然后我们来看元注解

元注解是自定义注解的组成要素。为了让你的注解能“按规矩贴在正确的地方”并且“能在运行时被读取”,必须在定义注解时时加上这两个基础元注解

第一个@Target:限制注解能放在哪

那它里面传什么呢,看一下kotlin的源码

要填的是 AnnotationTarget 这个枚举类的值。这个枚举包含了所有 Kotlin 代码中“可以被贴标签”的位置。

可以点开看一下

那我这里就规定他放在类/函数上

然后实验一下,发现确实

第二个 @Retention:决定注解活到什么时候

可以看到,默认是RUNTIME

来仔细看看,里面可以填些什么

SOURCE:只活在源码里,编译成 .class 文件就扔掉。

BINARY:保留到 .class 字节码里,但运行时 JVM 读取不到。

RUNTIME:不仅保留在字节码里,运行时还能通过反射读到

我不懂前两个,我只会无脑写成RUNTIME

自定义注解可以运行时通过反射来获取

定义两个只能放在类上,但Retention不同的注解

然后用反射来获取它,看看分别是什么结果

一个获取到了,一个因为Retention为SOURCE获取不到为null

下面是 @Repeatable:注解贴一次不够,还想贴好几次,(默认情况下,同一个位置只能贴一个相同的注解。加上这个就可以贴多次)

下面一个 @MustBeDocumented:告诉文档生成器“别漏了我”,生成 API 文档时(比如 KDoc 或 JavaDoc),有时候注解是有用信息,要展现在文档里。加上它就行

这个元注解非常“表面”,它不影响任何代码的运行逻辑,也不能通过反射读到。它唯一的用处,就是给 Dokka(Kotlin 的官方文档生成工具) 看的。

我那个plugins加载不出来,就先不安装演示了

然后呢,注解也是可以带参数的,但是他的参数是有限制的,

注解的参数,在编译期就必须是一个"固定值",要能写进 .class 字节码里,.class 文件是一个静态的二进制文件,它能存的东西非常有限:字符串、数字、布尔、枚举、类引用、以及这些的数组。,它存不了"对象",也存不了"运行时才确定的东西"。

注解参数存的东西,必须是编译期就能完全确定的常量——–>编译期常量(之后应该会讲一篇)

那到底哪些能放注解参数里呢!

1 基本数据类型 定义:val age: Int
使用:@Anno(18)
包括 Int, Long, Short, Byte, Float, Double, Boolean, Char
2 字符串 (String) 定义:val name: String
使用:@Anno("Tony")
必须是编译期确定的字符串(如字面量或 const val),不能是普通 var 变量。
3 类引用 (KClass) 定义:val clazz: KClass<*>
使用:@Anno(String::class)
Kotlin 用 ::class,Java 用 .class。这就是前面说的“类引用常量”。
4 枚举 (Enum) 定义:val color: Color
使用:@Anno(Color.RED)
必须是具体的某一个枚举项。
5 其他注解 定义:val inner: InnerAnno
使用:@Anno(InnerAnno("hi"))
注意: 传注解作为参数时,里面不能加 @ 符号。
6 上述类型的数组 定义:val arr: Array<String>
使用:@Anno(["A", "B"])
只能使用数组(Array, IntArray 等),绝对不能使用 List。

可以获取到他的

也可以获取

注解参数 = 编译期能确定、能直接写进 .class 的"死值"。所以:不能 null、不能集合、不能对象、不能算、不能变。想要"类"就存 KClass,想要"多个值"就存 Array,想要"可选"就给默认值。

不能放原因替代方案
null 常量池没有 null 给默认值
List / Map / Set 集合 运行时对象,存不进 .class Array<T>
自定义类实例 运行时 new 的 存 KClass<*>,运行时再建
运行时表达式 / 函数调用 非编译期常量 字面量或 const val
var 属性 注解只读 用 val
函数类型 () -> Unit 运行时对象 不支持,换设计

ok就扯到这 溜了 我真的特别爱你 为什么你会落泪

赞(0)
未经允许不得转载:171主机测评 » kotlin中对注解,元注解的探讨
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址