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"));
    }
}
相关推荐
Murphy_lx27 分钟前
C++ std_stringstream
开发语言·c++·算法
别或许29 分钟前
13.用户管理
android
v***870441 分钟前
QoS质量配置
开发语言·智能路由器·php
Wpa.wk1 小时前
自动化测试环境配置-java+python
java·开发语言·python·测试工具·自动化
道一231 小时前
C#获取操作系统版本号方法
开发语言·c#
道一231 小时前
C# 判断文件是否存在的方法
开发语言·c#
信仰_2739932431 小时前
Java面试题
java·开发语言
闫有尽意无琼1 小时前
银河麒麟v11 arm编译Qt creator8.0.2报错
开发语言·qt
小此方2 小时前
从零开始手搓堆:核心操作实现 + 堆排序 + TopK 算法+ 向上调整 vs 向下调整建堆的时间复杂度严密证明!
开发语言·数据结构·算法
_OP_CHEN2 小时前
从零开始的Qt开发指南:(五)Qt 常用控件之 QWidget(上):解锁 Qt 界面开发的核心基石
开发语言·c++·qt·前端开发·qwidget·gui开发·qt常用控件