Kotlin-Annotations详解

在Kotlin中,@注解(Annotations)是用于为代码提供元数据的一种机制。这些元数据可以被编译器、IDE(集成开发环境)或其他工具使用,以生成代码、执行编译时检查或提供运行时信息。

注解通常被用于类、方法、属性、构造函数、参数等

自定义注解

可以使用annotation关键字自定义自己的注解

自定义注解可以包含属性(也称为注解参数),这些属性在注解使用时被赋值

Kotlin 复制代码
annotation class HelloAnnotation(val value: String)

自定义注解的用途非常广泛,例如,它们可以用于标记测试方法、提供配置信息、指示代码生成等

使用自定义注解

Kotlin 复制代码
@HelloAnnotation("hello Annotation")
fun hello(){

}

反射注解

在运行时,可以通过反射(Reflection)读取函数注解信息

反射允许你检查类、方法、属性等上的注解,并读取它们的属性值

Kotlin 复制代码
    val function:KFunction<Unit> = ::hello
    val annotations:List<Annotation> = function.annotations
    for (annotation:Annotation in annotations) {
        if (annotation is HelloAnnotation) {
            println("Annotation: ${annotation.value}") //Annotation: hello Annotation
        }
    }

上面代码运行结果:

Annotation: hello Annotation

元注解

元注解(meta-annotations)是用于修饰其他注解的注解。它们定义了注解的行为和特性

常见的元注解包括:

  1. @Target
    • 指定注解可以应用的Kotlin元素类型,如类、方法、属性、参数等。
    • 如果不指定@Target,则注解可以应用于任何元素。
  2. @Retention
    • 指定注解在何时可用,即注解的生命周期。
    • AnnotationRetention.SOURCE:注解仅在源代码中保留,在编译时被丢弃。
    • AnnotationRetention.BINARY(默认值):注解在编译后的字节码中保留,但在运行时不可通过反射访问。
    • AnnotationRetention.RUNTIME:注解在运行时仍然可用,可以通过反射访问。
  3. @Repeatable
    • 允许同一个注解在同一个元素上被多次使用。
    • 要使注解可重复,你需要定义一个包含该注解类型的数组作为值的容器注解。
Kotlin 复制代码
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME)
annotation class HelloAnnotation(val value: String)

预定义注解

Kotlin 继承了Java的一些预定义注解,同时也有自己的一些预定义注解。以下是一些常见的预定义注解:

  1. @Deprecated:标记某个类、方法或属性已过时。你可以通过其messagereplaceWith参数来提供更多信息。

  2. @JvmOverloads:用于Kotlin函数,使其生成所有重载的Java方法(带有默认参数的函数)。

  3. @JvmStatic:用于Kotlin伴生对象的成员,使其作为Java静态方法或字段暴露。

  4. @JvmName:指定生成的Java字节码中元素的名称。

  5. @JvmMultifileClass:用于Kotlin的多文件类。

  6. @JvmField:指示Kotlin属性应该直接暴露为Java字段,而不是通过getter和setter。

  7. @file:JvmName:在文件顶部使用,为文件生成的所有类指定一个公共的Java类名。

    Kotlin 复制代码
    // @file:JvmName在文件最顶部使用
    @file:JvmName("Hello")
Kotlin 复制代码
// Hello.kt
package learnAnnotation

fun hello(string: String):String {
    return "Hello, $string"
}

上面是一个标准的kotlin文件

现在我们看看它实际在kotlin编译器中的样纸

java 复制代码
package learnAnnotation;

import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

@Metadata(
   mv = {2, 0, 0},
   k = 2,
   xi = 48,
   d1 = {"\u0000\n\n\u0000\n\u0002\u0010\u000e\n\u0002\b\u0002\u001a\u000e\u0010\u0000\u001a\u00020\u00012\u0006\u0010\u0002\u001a\u00020\u0001¨\u0006\u0003"},
   d2 = {"hello", "", "string", "learnKotlin"}
)
public final class HelloKt {
   @NotNull
   public static final String hello(@NotNull String string) {
      Intrinsics.checkNotNullParameter(string, "string");
      return "Hello, " + string;
   }
}

默认情况下,Kotlin编译器会为Hello.kt文件创建一个HelloKt类

比如我们直接在java文件中调用该函数时

java 复制代码
//Hi.java
package learnAnnotation;

public class Hi {
    public static void main(String[] args){
        System.out.println(HelloKt.hello("Annotation"));// Hello, Annotation
    }
}

如果不加Kt后缀:

如果这样的命名方式不符合你的调用习惯

这时候@file:JvmName就派上用场了,我们只需要将@file:JvmName放在文件最顶部即可

Kotlin 复制代码
// Hello.kt
// @file:JvmName在文件最顶部使用
@file:JvmName("Hello")

package learnAnnotation

fun hello(string: String):String {
    return "Hello, $string"
}

现在我们重新看下它的字节码

Kotlin 复制代码
package learnAnnotation;

import kotlin.Metadata;
import kotlin.jvm.JvmName;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

@Metadata(
   mv = {2, 0, 0},
   k = 2,
   xi = 48,
   d1 = {"\u0000\n\n\u0000\n\u0002\u0010\u000e\n\u0002\b\u0002\u001a\u000e\u0010\u0000\u001a\u00020\u00012\u0006\u0010\u0002\u001a\u00020\u0001¨\u0006\u0003"},
   d2 = {"hello", "", "string", "learnKotlin"}
)
@JvmName(
   name = "Hello"
)
public final class Hello {
   @NotNull
   public static final String hello(@NotNull String string) {
      Intrinsics.checkNotNullParameter(string, "string");
      return "Hello, " + string;
   }
}

已经被修改成了我们想要的样子

然后java中编译器的错误提示没有了

本例的完整代码

Kotlin 复制代码
//main.kt
package learnAnnotation

import kotlin.reflect.KFunction

fun main(args: Array<String>) {
    val function:KFunction<Unit> = ::hello
    val annotations:List<Annotation> = function.annotations
    for (annotation:Annotation in annotations) {
        if (annotation is HelloAnnotation) {
            println("Annotation: ${annotation.value}") //Annotation: hello Annotation
        }
    }
}

@HelloAnnotation("hello Annotation")
fun hello(){

}
Kotlin 复制代码
//hello.kt
@file:JvmName("Hello")
package learnAnnotation

fun hello(string: String):String {
    return "Hello, $string"
}
java 复制代码
//Hi.java
package learnAnnotation;

public class Hi {
    public static void main(String[] args){
        System.out.println(Hello.hello("Annotation"));
    }
}
相关推荐
尘浮生2 分钟前
Java项目实战II基于微信小程序的电影院买票选座系统(开发文档+数据库+源码)
java·开发语言·数据库·微信小程序·小程序·maven·intellij-idea
hopetomorrow16 分钟前
学习路之PHP--使用GROUP BY 发生错误 SELECT list is not in GROUP BY clause .......... 解决
开发语言·学习·php
小牛itbull25 分钟前
ReactPress vs VuePress vs WordPress
开发语言·javascript·reactpress
请叫我欧皇i34 分钟前
html本地离线引入vant和vue2(详细步骤)
开发语言·前端·javascript
闲暇部落37 分钟前
‌Kotlin中的?.和!!主要区别
android·开发语言·kotlin
GIS瞧葩菜1 小时前
局部修改3dtiles子模型的位置。
开发语言·javascript·ecmascript
chnming19871 小时前
STL关联式容器之set
开发语言·c++
熬夜学编程的小王1 小时前
【C++篇】深度解析 C++ List 容器:底层设计与实现揭秘
开发语言·数据结构·c++·stl·list
GIS 数据栈1 小时前
每日一书 《基于ArcGIS的Python编程秘笈》
开发语言·python·arcgis
Mr.131 小时前
什么是 C++ 中的初始化列表?它的作用是什么?初始化列表和在构造函数体内赋值有什么区别?
开发语言·c++