在Kotlin中,@注解(Annotations)是用于为代码提供元数据的一种机制。这些元数据可以被编译器、IDE(集成开发环境)或其他工具使用,以生成代码、执行编译时检查或提供运行时信息。
注解通常被用于类、方法、属性、构造函数、参数等
自定义注解
可以使用annotation关键字自定义自己的注解
自定义注解可以包含属性(也称为注解参数),这些属性在注解使用时被赋值
annotation class HelloAnnotation(val value: String)
自定义注解的用途非常广泛,例如,它们可以用于标记测试方法、提供配置信息、指示代码生成等
使用自定义注解
@HelloAnnotation("hello Annotation")
fun hello(){
}
反射注解
在运行时,可以通过反射(Reflection)读取函数注解信息
反射允许你检查类、方法、属性等上的注解,并读取它们的属性值
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)是用于修饰其他注解的注解。它们定义了注解的行为和特性
常见的元注解包括:
- @Target:
- 指定注解可以应用的Kotlin元素类型,如类、方法、属性、参数等。
- 如果不指定
@Target
,则注解可以应用于任何元素。
- @Retention:
- 指定注解在何时可用,即注解的生命周期。
AnnotationRetention.SOURCE
:注解仅在源代码中保留,在编译时被丢弃。AnnotationRetention.BINARY
(默认值):注解在编译后的字节码中保留,但在运行时不可通过反射访问。AnnotationRetention.RUNTIME
:注解在运行时仍然可用,可以通过反射访问。
- @Repeatable:
- 允许同一个注解在同一个元素上被多次使用。
- 要使注解可重复,你需要定义一个包含该注解类型的数组作为值的容器注解。
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME)
annotation class HelloAnnotation(val value: String)
预定义注解
Kotlin 继承了Java的一些预定义注解,同时也有自己的一些预定义注解。以下是一些常见的预定义注解:
-
@Deprecated
:标记某个类、方法或属性已过时。你可以通过其message
和replaceWith
参数来提供更多信息。 -
@JvmOverloads
:用于Kotlin函数,使其生成所有重载的Java方法(带有默认参数的函数)。 -
@JvmStatic
:用于Kotlin伴生对象的成员,使其作为Java静态方法或字段暴露。 -
@JvmName
:指定生成的Java字节码中元素的名称。 -
@JvmMultifileClass
:用于Kotlin的多文件类。 -
@JvmField
:指示Kotlin属性应该直接暴露为Java字段,而不是通过getter和setter。 -
@file:JvmName
:在文件顶部使用,为文件生成的所有类指定一个公共的Java类名。// @file:JvmName在文件最顶部使用 @file:JvmName("Hello")
// Hello.kt
package learnAnnotation
fun hello(string: String):String {
return "Hello, $string"
}
上面是一个标准的kotlin文件
现在我们看看它实际在kotlin编译器中的样纸
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文件中调用该函数时
//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放在文件最顶部即可
// Hello.kt
// @file:JvmName在文件最顶部使用
@file:JvmName("Hello")
package learnAnnotation
fun hello(string: String):String {
return "Hello, $string"
}
现在我们重新看下它的字节码
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中编译器的错误提示没有了
本例的完整代码
//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(){
}
//hello.kt
@file:JvmName("Hello")
package learnAnnotation
fun hello(string: String):String {
return "Hello, $string"
}
//Hi.java
package learnAnnotation;
public class Hi {
public static void main(String[] args){
System.out.println(Hello.hello("Annotation"));
}
}