开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第 1 天,点击查看活动详情
背景
最近阅读源码时,注意到这样一种注解(Annotation),
interface Component: ComponentIdentity {
// ...
@get:Incubating // <-- 这里
val sources: Sources
// ...
}
对这种新奇的用法比较陌生,遂有此文,以作记录。
分析过程
Google 搜索
根据经验,提取关键字 kotlin property annotation 进行搜索,得到结果如下:
一般通过 Google 搜索技术问题,返回的前几个结果通常是官方文档和 StackOverFlow,我一般会将它们都打开——这是我学习新知识的习惯做法:将官方文档和社区文章结合起来,来回阅读。
阅读官方文档
Kotlin Annotation 的官方文档链接:kotlinlang.org/docs/annota…
从官方文档页面,注意到这是 Annotation use-site targets,kotlinlang.org/docs/annota…
阅读社区文档
StackOverFlow 上的热门回答:stackoverflow.com/a/59925322
官方文档可能会比较难阅读,尤其当涉及到一些较为新的概念时。所以结合社区高质量的文档辅助去理解,很有必要。
正文
Kotlin 自动生成 Java Bytecode 规则
有个很基础的知识点:当定义了一个属性(Property),Kotlin 编译器会为该属性,生成一个 getter 和一个 setter(当且仅当是 var 类型属性时)方法。
Kotlin 中的一个属性,它可能出现的位置至少有:
- 构造函数参数列表
- 属性自身在类中的定义
getter方法setter方法(当且仅当是var类型属性)setter方法的参数列表
例如下面👇这块代码:
data class Person(
var name: String,
)
则对应的 Java 代码为:
public final class Person {
/**
* position 2
*/
private String name;
/**
* position 3
*/
public String getName() { return this.name; }
/**
* position 4
*/
public void setName(/* position 5 */String var1) { this.name = var1; }
public Person(/* position 1 */String name) { this.name = name; }
}
注1:省略了与本文非关键代码,如空检查、。
注2:使用 Kotlin 1.7.20 版本。
use-site targets
考虑上面这种情况,当为 Kotlin 中的一个属性添加注解时,这个注解应用到哪里了呢?还是凡该属性出现的位置,都会应用这个注解?
这似乎让注解有点表意不清,Kotlin 为解决这个问题,为注解引入 use-site targets 的概念,即允许通过关键字,指定注解实际作用的位置。这些关键字包括不限于:
param——作用于 Java 代码示例中的 position 1field——作用于 Java 代码示例中的 position 2get——作用于 Java 代码示例中的 position 3set——作用于 Java 代码示例中的 position 4setparam——作用于 Java 代码示例中的 position 5
更多的关键字在官网查看:kotlinlang.org/docs/annota…
继续为类 Person 为例,
data class Person(
@set:NameCheck // 注:这里 NameCheck 是一个注解
var name: String
)
对应的 Java 代码为:
public final class Person {
/**
* position 2
*/
private String name;
/**
* position 3
*/
public String getName() { return this.name; }
/**
* position 4
*/
@NameCheck // <-- 将会出现在这里
public void setName(/* position 5 */String var1) { this.name = var1; }
public Person(/* position 1 */String name) { this.name = name; }
}
缺省时的预期
一般不了解 use-site targets 的话,很多时候会这么写:
data class Person(
@SerializedName("name")
var name: String,
)
这时候 @SerializedName 注解会作用在哪里呢?看下对应的 Java 代码,
public final class Person {
/**
* position 2
*/
@SerializedName <-- 在这里
private String name;
/**
* position 3
*/
public String getName() { return this.name; }
/**
* position 4
*/
public void setName(/* position 5 */String var1) { this.name = var1; }
public Person(/* position 1 */String name) { this.name = name; }
}
查找官方文档发现,当未指定注解的作用对象时,将会根据注解的 @Target 所指定的类型,按照如下的优先顺序,应用在第一个可应用的对象,
parampropertyfield
总结
形如 @get:Incubating、@field:SerializedName 的用法为 Kotlin 的 Annotation use-site targets。它让使用者可以指定注解实际作用的对象,以实现更精准的语义和某些特定的用法。