Kotlin註解

註解聲明

註解是將元數據附加到代碼的方法。要聲明註解,請將 annotation{: .keyword } 修飾符放在類的前面:

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

用法

@Fancy class Foo {
    @Fancy fun baz(@Fancy foo: Int): Int {
        return (@Fancy 1)
    }
}

如果需要對類的主構造函數進行標註,則需要在構造函數聲明中添加 constructor{: .keyword} 關鍵字
,並將註解添加到其前面:

class Foo @Inject constructor(dependency: MyDependency) {
    // ……
}

你也可以標註屬性訪問器:

class Foo {
    var x: MyDependency? = null
        @Inject set
}

構造函數

註解可以有接受參數的構造函數。

annotation class Special(val why: String)

@Special("example") class Foo {}

允許的參數類型有:

  • 對應於 Java 原生類型的類型(Int、 Long等);
  • 字符串;
  • 類(Foo::class);
  • 枚舉;
  • 其他註解;
  • 上面已列類型的數組。

註解參數不能有可空類型,因爲 JVM 不支持將 null 作爲
註解屬性的值存儲。

如果註解用作另一個註解的參數,則其名稱不以 @ 字符爲前綴:

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 代碼能夠正常看到該註解和參數


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 這樣的框架很有用,
該框架使用註解進行併發控制。

annotation class Suspendable

val f = @Suspendable { Fiber.sleep(10) }

註解使用處目標

當對屬性或主構造函數參數進行標註時,從相應的 Kotlin 元素
生成的 Java 元素會有多個,因此在生成的 Java 字節碼中該註解有多個可能位置
。如果要指定精確地指定應該如何生成該註解,請使用以下語法:

class Example(@field:Ann val foo,    // 標註 Java 字段
              @get:Ann val bar,      // 標註 Java getter
              @param:Ann val quux)   // 標註 Java 構造函數參數

可以使用相同的語法來標註整個文件。 要做到這一點,把帶有目標 file 的註解放在
文件的頂層、package 指令之前或者在所有導入之前(如果文件在默認包中的話):

@file:JvmName("Foo")

package org.jetbrains.demo

如果你對同一目標有多個註解,那麼可以這樣來避免目標重複——在目標後面添加方括號
並將所有註解放在方括號內:

class Example {
     @set:[Inject VisibleForTesting]
     var collaborator: Collaborator
}

支持的使用處目標的完整列表爲:

  • file
  • property(具有此目標的註解對 Java 不可見)
  • field
  • get(屬性 getter)
  • set(屬性 setter)
  • receiver(擴展函數或屬性的接收者參數)
  • param(構造函數參數)
  • setparam(屬性 setter 參數)
  • delegate(爲委託屬性存儲其委託實例的字段)

要標註擴展函數的接收者參數,請使用以下語法:

fun @receiver:Fancy String.myExtension() { }

如果不指定使用處目標,則根據正在使用的註解的 [@Target](https://github.com/Target "@Target") 註解來選擇目標
。如果有多個適用的目標,則使用以下列表中的第一個適用目標:

  • param
  • property
  • field

Java 註解

Java 註解與 Kotlin 100% 兼容:

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 編寫的註解沒有定義參數順序,所以不能使用常規函數調用
語法來傳遞參數。相反,你需要使用命名參數語法。

// Java
public @interface Ann {
    int intValue();
    String stringValue();
}
// Kotlin
@Ann(intValue = 1, stringValue = "abc") class C

就像在 Java 中一樣,一個特殊的情況是 value 參數;它的值無需顯式名稱指定。

// Java
public @interface AnnWithValue {
    String value();
}
// Kotlin
@AnnWithValue("abc") class C

如果 Java 中的 value 參數具有數組類型,它會成爲 Kotlin 中的一個 vararg 參數:

// Java
public @interface AnnWithArrayValue {
    String[] value();
}
// Kotlin
@AnnWithArrayValue("abc", "foo", "bar") class C

對於具有數組類型的其他參數,你需要顯式使用 arrayOf

// Java
public @interface AnnWithArrayMethod {
    String[] names();
}
// Kotlin
@AnnWithArrayMethod(names = arrayOf("abc", "foo", "bar")) class C

註解實例的值會作爲屬性暴露給 Kotlin 代碼。

// Java
public @interface Ann {
    int value();
}
// Kotlin
fun foo(ann: Ann) {
    val i = ann.value
}