注解聲明
注解是將元數據附加到代碼的方法。要聲明注解,請將 annotation 修飾符放在類的前面:
1
|
annotation class Fancy |
注解的附加屬性可以通過用元注解標注注解類來指定:
- @Target 指定可以用 該注解標注的元素的可能的類型(類、函數、屬性、表達式等);
- @Retention 指定該注解是否 存儲在編譯后的 class 文件中,以及它在運行時能否通過反射可見 (默認都是 true);
- @Repeatable 允許 在單個元素上多次使用相同的該注解;
- @MustBeDocumented 指定 該注解是公有 API 的一部分,并且應該包含在 生成的 API 文檔中顯示的類或方法的簽名中。
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION,
AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.EXPRESSION)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
annotation class Fancy
用法
1
2
3
4
5
|
@Fancy class Foo { @Fancy fun baz( @Fancy foo: Int): Int { return ( @Fancy 1 ) } } |
如果需要對類的主構造函數進行標注,則需要在構造函數聲明中添加 constructor 關鍵字 ,并將注解添加到其前面:
1
2
3
|
class Foo @Inject constructor(dependency: MyDependency) { // …… } |
你也可以標注屬性訪問器:
1
2
3
4
|
class Foo { var x: MyDependency? = null @Inject set } |
構造函數
注解可以有接受參數的構造函數。
1
2
3
|
annotation class Special(val why: String) @Special ( "example" ) class Foo {} |
允許的參數類型有:
- 對應于 Java 原生類型的類型(Int、 Long等);
- 字符串;
- 類(Foo::class);
- 枚舉;
- 其他注解;
- 上面已列類型的數組。
注解參數不能有可空類型,因為 JVM 不支持將 null 作為 注解屬性的值存儲。
如果注解用作另一個注解的參數,則其名稱不以 @ 字符為前綴:
1
2
3
4
5
6
|
annotation class ReplaceWith(val expression: String) annotation class Deprecated( val message: String, val replaceWith: ReplaceWith = ReplaceWith( "" )) @Deprecated ( "This function is deprecated, use === instead" , ReplaceWith( "this === other" )) |
如果需要將一個類指定為注解的參數,請使用 Kotlin 類 (KClass)。Kotlin 編譯器會 自動將其轉換為 Java 類,以便 Java 代碼能夠正常看到該注解和參數 。
1
2
3
4
5
|
import kotlin.reflect.KClass annotation class Ann(val arg1: KClass<*>, val arg2: KClass<out Any?>) @Ann (String:: class , Int:: class ) class MyClass |
Lambda 表達式
注解也可以用于 lambda 表達式。它們會被應用于生成 lambda 表達式體的 invoke() 方法上。這對于像 Quasar這樣的框架很有用, 該框架使用注解進行并發控制。
1
2
3
|
annotation class Suspendable val f = @Suspendable { Fiber.sleep( 10 ) } |
注解使用處目標
當對屬性或主構造函數參數進行標注時,從相應的 Kotlin 元素 生成的 Java 元素會有多個,因此在生成的 Java 字節碼中該注解有多個可能位置 。如果要指定精確地指定應該如何生成該注解,請使用以下語法:
1
2
3
|
class Example( @field :Ann val foo, // 標注 Java 字段 @get :Ann val bar, // 標注 Java getter @param :Ann val quux) // 標注 Java 構造函數參數 |
可以使用相同的語法來標注整個文件。 要做到這一點,把帶有目標 file 的注解放在 文件的頂層、package 指令之前或者在所有導入之前(如果文件在默認包中的話):
1
2
3
|
@file :JvmName( "Foo" ) package org.jetbrains.demo |
如果你對同一目標有多個注解,那么可以這樣來避免目標重復——在目標后面添加方括號 并將所有注解放在方括號內:
1
2
3
4
|
class Example { @set :[Inject VisibleForTesting] var collaborator: Collaborator } |
支持的使用處目標的完整列表為:
- file
- property(具有此目標的注解對 Java 不可見)
- field
- get(屬性 getter)
- set(屬性 setter)
- receiver(擴展函數或屬性的接收者參數)
- param(構造函數參數)
- setparam(屬性 setter 參數)
- delegate(為委托屬性存儲其委托實例的字段)
要標注擴展函數的接收者參數,請使用以下語法:
1
|
fun @receiver :Fancy String.myExtension() { } |
如果不指定使用處目標,則根據正在使用的注解的 @Target 注解來選擇目標 。如果有多個適用的目標,則使用以下列表中的第一個適用目標:
- param
- property
- field
Java 注解
Java 注解與 Kotlin 100% 兼容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import org.junit.Test import org.junit.Assert.* import org.junit.Rule import org.junit.rules.* class Tests { // 將 @Rule 注解應用于屬性 getter @get :Rule val tempFolder = TemporaryFolder() @Test fun simple() { val f = tempFolder.newFile() assertEquals( 42 , getTheAnswer()) } } |
因為 Java 編寫的注解沒有定義參數順序,所以不能使用常規函數調用 語法來傳遞參數。相反,你需要使用命名參數語法。
1
2
3
4
5
6
7
|
// Java public @interface Ann { int intValue(); String stringValue(); } // Kotlin @Ann (intValue = 1 , stringValue = "abc" ) class C |
就像在 Java 中一樣,一個特殊的情況是 value 參數;它的值無需顯式名稱指定。
1
2
3
4
5
6
|
// Java public @interface AnnWithValue { String value(); } // Kotlin @AnnWithValue ( "abc" ) class C |
如果 Java 中的 value 參數具有數組類型,它會成為 Kotlin 中的一個 vararg 參數:
1
2
3
4
5
6
|
// Java public @interface AnnWithArrayValue { String[] value(); } // Kotlin @AnnWithArrayValue ( "abc" , "foo" , "bar" ) class C |
對于具有數組類型的其他參數,你需要顯式使用 arrayOf:
1
2
3
4
5
6
|
// Java public @interface AnnWithArrayMethod { String[] names(); } // Kotlin @AnnWithArrayMethod (names = arrayOf( "abc" , "foo" , "bar" )) class C |
注解實例的值會作為屬性暴露給 Kotlin 代碼。
1
2
3
4
5
6
7
8
|
// Java public @interface Ann { int value(); } // Kotlin fun foo(ann: Ann) { val i = ann.value } |
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
原文鏈接:https://coyee.com/article/12299-kotlin-annotations