注解

定義注解

Kotlin使用 annotation class 關鍵字(就像使用 enum class 定義枚舉類一樣),定義注解非常簡單,Kottin 甚至不允許為注解定義注解體,也就是說,注解后面不能有花括號。

//定義一個簡單的注解
annotation class MyClass

定義了該注解之后,就可以在程序的任何地方使用該注解。使用注解的語法非常類似于使 用 public、 final 這樣的修飾符,通常可用于修飾程序中的類、方法、屬性、接口等定義。通常會把注解放在所有修飾符之前。

//使用自Test 修飾類定義
@Test
class Demo1 {
    //使用自Test 注解修飾屬性
    @Test
    var name: String = ""

    //使用自Test 注解修飾方法 
    @Test
    fun info() {

    }
}

如果要用注解來修飾主構造器,就像前面所介紹的,程序必須為主構造器添加 constructor 關鍵字。

class User @Test constructor(var name : String, var pass: String) { }

注解的屬性和構造器

注解還可以帶屬性,由于注解沒有注解體,因此注解的屬性只能在注解聲明部分指定。實際上,相當于在注解的主構造器中指定注解的屬性。

由于注解與普通類不同 , 注解的屬性值只能在使用時指定,并且一旦為注解的屬性指定了屬性值,以后就絕對不會改變其屬性值,因此注解的屬性只能定義為只讀屬性。

annotation class MyTag(val name: String , val age : Int)

使用 annotation class 定義的注解其實就相當于定義了一個注解接口,這個注解接口繼承了kotlin.Annotation接口。
需要說明的是,注解的屬性不能使用可空類型(不能在類型后添加“?”),這是因為JVM本身不允許使用 null作為注解的屬性值。

一旦在注解中定義了屬性之后 ,使用該屬性時就應該為其指定屬性值 ,如下面代碼所示

class Item {
    //使用帶屬性的注解時,需要為屬性指定屬性值 
    @MyTag(name="xx", age=6)
    fun info() {
    }
}

也可以在定義注解的屬性時使用等號(=)為其指定初始值(默認值)(就像定義類時在主構造器中為類的屬性指定初始值 一樣) ,注解的初始值只能是編譯時常量。如果為注解的屬性指定了默認值,那么在使用該注解時可以不為這些屬性指定值,而是直接使用默認值。

根據注解是否可以包含屬性,可以把注解分為如下兩類。

  • 標記注解: 沒有定義屬性的注解被稱為標記注解。這種注解僅利用自身的存在與否來提供信息,如前面所介紹的@Test等注解。
  • 元數據注解: 包含屬性的注解被稱為元數據注解。因此它們可以接受更多的配置信息(以屬性值的方式進行設置) 。 如前面所介紹的@MyTag等注解。

與 Java類似的是,如果注解的屬性名為 value,則為 value屬性指定屬性值時可省略屬性名。

Kotlin使用 vararg修飾需要指定多個值的屬性(相當于數組類型的屬性),也可以不帶屬性名。

如果將一個注解作為另一個注解的屬性值,那么在使用注解時不需要以@作為前綴。

//定義帶 value 屬性的注解
annotation class MyTag(val value: String)

//該注解的 target 屬性的類型是 MyTag
annotation class showTag(val message:String,val tag:MyTag)


@showTag(message = "SS",tag = MyTag("ZZZ"))
class Demo1

如果需要將一個類作為注解的屬性,請使用 Kotlin 類( KClass), Kotlin 編譯器會自動將 其轉換為 Java類,以便 Java代碼能夠正常看到該注解和參數 。

// tag1 的類型是 KClass<*>,這是星號投影用法,相當于 Java 的原始類型
// tag2 的類型是 KClass<out Any>,這是使用處協變的用法
//可傳入 KClass<Int>、 KClass<String〉等,只要尖括號里的類型是 Any 的子類即可
annotation class DrawTag(val tag1:KClass<*>,val tag2:KClass<out Any>)

@DrawTag(tag1 = String::class,tag2 = Int::class)
class Demo1

元注解

Kotlin 在 kotlin.annotation 包下提供了4個Meta 注解(元注解),這4個元注解都用于修飾其他的注解定義。

使用@ Retention

@Retention只能修飾注解定義,用于指定被修飾的注解可以保留多長時間 。@Retention元注解包含一個 AnnotationRetention類型的 value屬性,所以使用@Retention時必須為該value 屬性指定值。
value 屬性的值只能是如下 3 個 。

  • AnnotationRetention.SOURCE: 注解只保留在源代碼中,編譯器直接丟棄這種注解 。
  • AnnotationRetention.BINARY: 編譯器將把注解記錄在 class 文件中 。當運行該字節碼
    文件時, JVM 不可獲取注解信息。
  • AnnotationRetention.RUNTIME: 編譯器將把注解記錄在 class文件中。當運行該字節
    碼文件時, JVM也可獲取注解信息,程序可以通過反射獲取該注解信息。這是默認值。

如果要通過反射獲取注解信息,就需要使用 value屬性值為 AnnotationRetention.RUNTIME
的@Retention (或省略該元注解)。使用@Retention元注解可采用如下代碼為value指定值。

//下面定義的 Testable 注解保留到運行時
@Retention(value = AnnotationRetention.RUNTIME)
annotation class Test

使用@Target

@Target 也只能修飾注解定義,用于指定被修飾的注解能修飾哪些程序單元。@Target 元注解包含一個類型為 AnnotationTarget 數組的 allowedTargets 屬性,該屬性的值只能是如下幾個值組成的數組。

  • Annotation Target.CLASS: 指定該策略的注解只能修飾類。
  • AnnotationTarget.ANNOTATION_CLASS:指定該策略的注解只能修飾注解。
  • AnnotationTarget.TYPE_PARAMETER:指定該策略的注解只能修飾泛型形參(目前暫時還不支持)。
  • AnnotationTarget.PROPERTY:指定該策略的注解只能修飾屬性。
  • AnnotationTarget.FIELD: 指定該策略的注解只能修飾字段(包括屬性的幕后字段)。
  • AnnotationTarget.LOCAL_VARIABLE:指定該策略的注解只能修飾局部變量。
  • AnnotationTarget.VALUE_PARAMETER:指 定該策略的注解只能修飾函數或構造器的形參。
  • AnnotationTarget.CONSTRUCTOR: 指定該策略的注解只能修飾構造器。
  • AnnotationTarget.FUNCTION: 指定該策略的注解只能修飾函數和方法(不包含構造器)。
  • AnnotationTarget. PROPERTY_GETTER:指 定該策略的注解只能修飾屬性的getter 方法。
  • AnnotationTarget. PROPERTY_SETTER:指 定該策略的注解只能修飾屬性的setter 方法。
  • Annotation Target.TYPE: 指定該策略的注解只能修飾類型。
  • AnnotationTarget.EXPRESSION: 指定該策略的注解只能修飾各種表達式。
  • AnnotationTarget.FILE: 指定該策略的注解只能修飾文件。
  • AnnotationTarget.TYPEALIAS:指定該策略的注解只能修飾類型別名。

與使用@Retention 類似的是,使用@Target 也可以直接在括號里指定value 值,而無須使用name=value 的形式。如下代碼指定@ActionListenerFor注解只能修飾屬性。

//指定@ActionListenerFor注解只能修飾屬性
@Target(allowedTargets = AnnotationTarget.PROPERTY)
annotation class ActionListenerFor

使用@MustBeDocumented

使用@MustBeDocumented元注解修飾的注解將被文檔工具提取到API文檔中,如果定義注解類時使用了@MustBeDocumented修飾,則所有使用該元注解修飾的程序元素的API文檔中將會包含該注解說明 。
下面代碼定義了一個@Testable 注解,程序使用@MustBeDocumented 修飾該注解,所以@Testable 注解將被文檔工具所提取。

//指定@ActionListenerFor注解只能修飾屬性
@Retention(value = AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
//定義@Testable 注解將被文檔工具所提取
@MustBeDocumented
annotation class Testable

上面代碼指定了文檔工具生成 API 文檔時將提取@Testable的使用信息。下面代碼定義了一個 MyTest類,該類中的 info()方法使用了@Testable修飾。

class MyTest {
   //使用自Testable修飾info()方法
   @Testable
   private fun info() {
       println("info")

   }
}

使用@Repeatable標記可重復注解

Kotlin允許使用多個相同的注解來修飾同一個程序單元,這種注解稱為可重復注解 。
開發可重復注解需要使用@Repeatable修飾,下面通過示例來介紹如何開發可重復注解。首先定義一個@FkTag注解。

//指定@ActionListenerFor注解只能修飾屬性
@Retention(value = AnnotationRetention.SOURCE)
@Target(AnnotationTarget.CLASS)
@Repeatable
annotation class FkTag(val name:String="kotlin",val age:Int)

上面定義了@FkTag注解,該注解包含兩個屬性。程序還使用了@Repeatable 來修飾該注解,這意味著它是一個可重復注解,因此可直接使用多個@FkTag 注解修飾目標程序單元。

@FkTag(name = "xq",age = 24)
@FkTag(age = 4)
class MyTest {
    private fun info() {
        println("info")

    }
}

需要說明的是,由于在Java 8 之前JVM 并不支持可重復注解,Kotlin 也沒有辦法突破該限制,因此可重復注解的@Retention 策略只能指定為 AnnotationRetention.SOURCE,這意味著可重復注解只能被 Kotlin 編譯器讀取,接下來 Kotlin 編譯器會直接丟棄該注解信息。

使用注解

提取注解信息

使用注解修飾類、方法、屬性等成員之后,這些注解不會自己生效,必須由開發者提供相應的工具來提取并處理注解信息。
Kotlin使用 kotlin.Annotation接口來代表程序元素前面的注解,該接口是所有注解的父接口。Kotlin 在kotlin.reflect 包下新增了KAnnotatedElement接口,該接口代表程序中可以接受注解的程序元素。該接口主要有如下幾個實現類。

  • KCallable: 代表可執行的程序實體,如函數和屬性。
  • KClass:代表 Kotlin 的類、接口等類型。
  • KParameter: 代表函數和屬性的參數。

在kotlin.reflect包下主要包含一些實現反射功能的工具類,該包所提供的反射API包含了讀取運行時注解的能力。只有當定義注解時使用了@Retention(AnnotationRetention.RUNTIME) 修飾,該注解才會保留到程序運行時,JVM 才會在裝載* .class 文件時讀取保存在 class 文件中的注解。

KAnnotatedElement接口是所有程序元素(如 KClass、 KCallable、 KParameter)的父接口, 所以程序通過反射獲取了某個程序單元對應的KAnnotatedElement對象(如 KClass、KCallable、 KParameter)之后,程序就可以調用該對象的如下屬性和方法來訪問注解信息。

  • annotations: List<Annotation>: 該屬性返回該程序單元上所有的注解。
  • <T: Annotation> findAnnotation(): T?: 根據注解類型返回該程序單元上特定類型的注解。如果該類型的注解不存在,則該方法返回 null。

下面代碼片段用于獲取 Test類中修飾 info()方法的所有注解,井將這些注解打印出來。

//指定@ActionListenerFor注解只能修飾屬性
@Retention(value = AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
//定義@Testable 注解將被文檔工具所提取
@MustBeDocumented
annotation class Testable


class MyTest {
    //使用自Testable修飾info()方法
    @Testable
    public fun info() {
        println("info")

    }
}

fun main(args: Array<String>) {
    val aArray = MyTest::info.annotations
    //遍歷所有注解
    for (an in aArray){
        println(an)
    }
}

如果需要獲取某個注解里的元數據,則可以將注解轉型成所需的注解類型,然后通過注解對象的屬性來訪問這些元數據。代碼如下 :

//指定@ActionListenerFor注解只能修飾屬性
@Retention(value = AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
//定義@Testable 注解將被文檔工具所提取
@MustBeDocumented
annotation class Testable(val name:String="xq",val age:Int =24)


class MyTest {
    //使用自Testable修飾info()方法
    @Testable(name = "sq",age = 23)
    fun info() {
        println("info")

    }
}

fun main(args: Array<String>) {
    val aArray = MyTest::info.annotations
    //遍歷所有注解
    for (an in aArray){
        println(an)
        if(an is Testable){
            println(an.name)
            println(an.age)
        }
    }
}

下面分別介紹兩個使用注解的例子。第 一個例子中的@Testable 注解沒有任何屬性,它僅是一個標記注解,其作用是標記哪些方法需要測試。

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
//定義一個標記注解,不包含任何屬性
annotation class Testable

class Test {
    //使用@ Testable 注解指定該方法是需要測試的
    @Testable
    fun m1() {

    }

    fun m2() {

    }

    //使用@ Testable 注解指定該方法是需要測試的
    @Testable
    fun m3() {

    }

    fun m4() {

    }

    //使用@ Testable 注解指定該方法是需要測試的
    @Testable
    fun m5() {

    }

    fun m6() {

    }

    //使用@ Testable 注解指定該方法是需要測試的
    @Testable
    fun m7() {

    }

    fun m8() {

    }
}

正如前面所提到的,僅僅使用注解來標記程序元素對程序是不會有任何影響的,這也是注解的一條重要原則。為了讓程序中的這些注解起作用,接下來必須為這些注解提供一個注解處理工具。

下面的注解處理工具會分析目標類,如果目標類中的方法使用了@Testable 注解修飾,則通過反射來運行該測試方法。

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
//定義一個標記注解,不包含任何屬性
annotation class Testable

class Test {
    //使用@ Testable 注解指定該方法是需要測試的
    @Testable
    fun m1() {

    }

    fun m2() {

    }

    //使用@ Testable 注解指定該方法是需要測試的
    @Testable
    fun m3() {
        throw RuntimeException ("參數出錯了! ")
    }

    fun m4() {

    }

    //使用@ Testable 注解指定該方法是需要測試的
    @Testable
    fun m5() {

    }

    fun m6() {

    }

    //使用@ Testable 注解指定該方法是需要測試的
    @Testable
    fun m7() {
        throw RuntimeException ("程序業務出現異常! ")
    }

    fun m8() {

    }
}

inline fun <reified T : Any> processTestable() {
    var  passed =0
    var failed = 0
    val target = T::class.createInstance()
    //遍歷 T 對應的類里的所有方法
    for (m in T::class.functions) {
        //如果該方法使用了@ Testable 修飾
        if (m.findAnnotation<Testable>() != null) {
            try{
                //調用 m方法
                m.call(target)
                //測試成功, passed 計數器加 1
                passed++
            }catch (ex:Exception){
                println ("方法"+ m +"運行失敗,異常:" + ex.cause)
                //測試出現異常, failed 計數器加 1
                failed++

            }
        }
    }

    //統計測試結果
    println ("共運行了:"+ (passed + failed) +"個方法,其中:\n"+"失敗了:"+ failed +"個,\n" +"成功了:"+ passed +"個!")
}

fun main(args: Array<String>) {
    //處理 MyTest 類
    //運行結果:方法fun test10.Test.m3(): kotlin.Unit運行失敗,異常:java.lang.RuntimeException: 參數出錯了! 
    //方法fun test10.Test.m7(): kotlin.Unit運行失敗,異常:java.lang.RuntimeException: 程序業務出現異常! 
    //共運行了:4個方法,其中:
    //失敗了:2個,
    //成功了:2個!
    processTestable<Test>()
}

上面程序定義了 一個<reifiedT: Any> processTestable()函數, 該函數可接收一個泛型參數, 分析該泛型參數所代表的類,并運行該目標類中使用@Testable修飾的方法。

前面介紹的只是一個標記注解,程序通過判斷該注解存在與否來決定是否運行指定方法。下面程序通過使用注解來簡化事件編程。在傳統的事件編程中總是需要通過addActionListener() 方法來為事件源綁定事件監聽器,本示例則通過@ActionListenerFor 來為程序中的按鈕綁定事件監聽器。

package test10

import java.awt.event.ActionEvent
import java.awt.event.ActionListener
import javax.swing.JButton
import javax.swing.JFrame
import javax.swing.JOptionPane
import javax.swing.JPanel
import javax.swing.WindowConstants.EXIT_ON_CLOSE
import kotlin.reflect.KClass

//指定該注解只能修飾屬性
@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME)
//定義一個屬性,用于設置元數據
//該 listener 屬性用于保存監聽器實現類
annotation class ActionListenerFor(val listener: KClass<out ActionListener>)

class AnnotationTest {
    val mainWin = JFrame("使用注解綁定事件監聽器")
    //使用注解為 ok 按鈕綁定事件監聽器
    @ActionListenerFor(listener = OkListener::class)
    val ok = JButton("確定")

    //使用注解為 cancel 按鈕綁定事件監聽器
    @ActionListenerFor(listener = CancelListener::class)
    val cancel = JButton("取消")

    fun init() {
        //初始化界面的方法
        val jp = JPanel()
        jp.add(ok)
        jp.add(cancel)
        mainWin.add(jp)
        processAnnotations(this)
        mainWin.defaultCloseOperation = EXIT_ON_CLOSE
        mainWin.pack()
        mainWin.isVisible = true
    }
}

//定義 ok 按鈕的事件監聽器實現類
class OkListener : ActionListener {
    override fun actionPerformed(e: ActionEvent?) {
        JOptionPane.showMessageDialog(null, "單擊了確認按鈕")
    }

}

//定義cancel按鈕的事件監聽器實現類
class CancelListener : ActionListener {
    override fun actionPerformed(e: ActionEvent?) {
        JOptionPane.showMessageDialog(null, "單擊了取消按鈕")
    }

}

fun main(args: Array<String>) {
    AnnotationTest().init()
}

//處理注解的方法,其中 obj 是包含注解的對象
fun processAnnotations(obj: Any) {
    //獲取 obj 對象的類
    val cl = obj::class
    //獲取指定 obj對象的所有成員,并遍歷每個成員
    for (prop in cl.memberProperties) {
        //獲取該成員上 ActionListenerFor 類型的注解
        val a = prop.findAnnotation<ActionListenerFor>()
        //獲取屬性 prop 的值
        val fObj = prop.call(obj)

        //如果 fObj 是 AbstractButton的實例,且 a 不為 null
        if (a != null && fObj != null && fObj is AbstractButton) {
            //獲取 a 注解的 listener 屬性值〈它是一個監聽器類〉
            val listenerClazz = a.listener
            //使用反射來創建 listener 類的對象
            val al = listenerClazz.createInstance()
            //為 fObj 按鈕添加事件監聽器
            fObj.addActionListener(al)
        }

    }
}

上面代碼定義了兩個 JButton 按鈕,并使用@ActionListenerFor 注解為這兩個按鈕綁定了事件監聽器。使用@ActionListenerFor 注解時傳入了 listener 元數據,該元數據用于設定每個按鈕的監聽器實現類。

正如前面所提到的,如果僅在程序中使用注解是不會起任何作用的,必須使用注解處理工具來處理程序中的注解。 上面代碼使用了 processAnnotations()函數來處理注解,該處理器分析目標對象中的所有屬性,如果在屬性前使用了@ActionListenerFor 修飾,則取出該注解中的listener元數據,并根據該元數據來綁定事件監聽器。

Java 注解與 Kotlin 的兼容性

Java 注解與 Kotlin 完全兼容,只是在使用時略加注意即可。

指定注解的作用目標

根據前面的介紹我們知道, Kotlin程序往往比較簡潔, Kotlin程序的一個程序單元有時候會變成Java的多個程序單元。比如:

  • 帶屬性聲明的主構造器會變成 Java 的成員變量定義、 getter方法、 setter方法(如果是讀寫屬性)、構造器參數 。
  • 屬性會變成 Java 的成員變量定義、 getter方法、 setter方法(如果是讀寫屬性) 。

這樣就產生了一個問題: 有時候我們只想用注解修飾特定的程序單元,比如只希望用注解修飾屬性對應的幕后字段,或者只希望用注解修飾屬性對應的getter方法,那該怎么辦呢?

此時就需要為注解指定作用目標,語法格式如下:

@目標 :注解(注解屬性值)

如果在同 一個目標上要指定多個注解,則需要將多個注解放在方括號中,并用空格隔開,語法格式如下:

@目標 : [注解 1 (注解屬性值)注解 2 (注解屬性值), . . . ]

從上面的語法格式不難看出,為注解指定作用目標,其實就是在@符號和注解之間添加目標名和冒號。 Kotlin支持的目標包含如下幾個。

  • file: 指定注解對文件本身起作用 。
  • property: 指定注解對整個屬性起作用(這種目標的注解對 Java 不可見,因為 Java 并沒有真正的屬性) 。
  • field: 指定注解對屬性的幕后字段起作用。
  • get: 指定注解對屬性的 getter方法起作用。
  • set: 指定注解對屬性的 setter方法起作用 。
  • receiver: 指定注解對擴展方法或擴展屬性的接收者起作用 。
  • param: 指定注解對構造器的參數起作用。
  • setparam: 指定注解對 setter方法的參數起作用。
  • delegate: 指定注解對委托屬性存儲其委托實例的字段起作用。

下面先看一個簡單的例子,程序指定注解只對屬性的 getter方法起作用。

annotation class MyTag
annotation class FkTag(val info: String)

class Item {
    //指定注解只對 getter 方法起作用
    //對 getter 方法應用了兩個注解: MyTag、 FkTag
    @get:[MyTag FkTag(info = "補充信息")]
    var name = "kotlin"
}

fun main(args: Array<String>) {

    //獲取Item類對應的Java類 (Class對象)
    val clazz = Item::class.java
    //遍歷 clazz 類所包含的全部方法
    for (mtd in clazz.declaredMethods) {
        println("一方法${mtd}上的注解如下一")
        //遍歷該方法上直接聲明的所有注解
        for (an in mtd.declaredAnnotations) {
            println(an)
        }
    }

    //遍歷 clazz 類所包含的全部成員變量
    for (f in clazz.declaredFields) {
        println("一屬性${f}上的注解如下一")
        //遍歷該成員變盤上直接聲明的所有注解
        for (an in f.declaredAnnotations) {
            println(an)
        }
    }
}

上面代碼指定 name屬性的 getter方法應用了兩個注解,其實就是在原來的注解用法前增加@get:部分,與原來不指定目標的注解區別并不大。

程序后面的 main()函數主要就是分析 Item 類中各方法、成員變量的注解 。 由于要分析成員變量上的注解,因此 main()函數使用了 Java 的反射 API (由于 Kotlin 并不支持單獨 定義成員變量,因此 Kotlin 的反射 API 不支持直接操作成員變量),通過該 main()的運行可以看到程序中添加的兩個注解只作用于屬性的getter方法上 。

如果要指定注解作用于整個文件本身,則必須將注解放在 package 語句(如果有 package語句)之前,或者所有導包語句之前(如果沒有package 語句) 。代碼如下 :

//指定自 FileTag 注解作用于整個文件
@file: FileTag ("yeeku") 
package org.crazyit.demo

如果要指定注解作用于擴展方法或擴展屬性的接收者,則使用帶 receiver:的注解修飾整個擴展方法或擴展屬性即可。例如如下代碼:

// 指定@MyTag 注解作用于擴展方法的接收者(String) 
fun @receiver:MyTag String.fun() {}

使用Java注解

Kotlin完全兼容Java注解,因此可以直接在 Kotlin程序中使用Java注解。
需要說明的是,Java注解的成員變量 (相當于 Kotlin 注解的屬性,后文統稱為“屬性”) 是沒有順序的,因此只能通過屬性名來設置屬性值 CKotlin注解的屬性還可通過位置來設置屬性值) 。

對比如下兩個注解。 下面先定義一個 Java注解。

public @interface JavaTag {
    String name();

    int age();
}

下面再定義一個 Kotlin注解。

annotation class KotlinTag(val name:String,val age:Int)

上面兩個注解基本相同,它們都包含了 name 和 age 兩個屬性 。接下來在程序中使用這兩個注解就可看到它們的區別 。

//Kotlin 注解可通過位置來指定屬性值
//第一個值傳給 name 屬性,第二個值傳給 age 屬性
@KotlinTag("kotlin", 4)
class Book {
    //Kotlin 注解也可通過屬性名來指定屬性值
    @KotlinTag(name = "xq", age = 24)
    //Java注解只能通過屬性名來指定屬性值
    @JavaTag(name = "xy", age = 22)
    fun test() {
        
    }
}

Kotlin 注解既支持使用位置來指定屬性值(第一個值傳給第一個屬性,第二個值傳給第二個屬性,依此類推),也支持使用屬性名來指定屬性值(這是傳統 Java注解的使用方式〉;而 Java注解只能通過屬性名來指定屬性值 。

如果 Java 注解中的 value 屬性是數組類型,那么它會變成 Kotlin 注解的 vararg屬性,因此直接為它傳入多個屬性值即可 。

public @interface JavaTag {
    String[] value();
}

數組類型的 value 屬性會變成 Kotlin 注解的 vararg屬性,因此可以在 Kotlin 程序中按如下方式使用該注解 。

@JavaTag("kotlin", "java")
class Book 

但如果其他名稱的屬性是數組類型,那么在 Kotlin 中使用該注解時必須顯式使用 arrayOf() 函數來構建數組。例如如下 Java注解。

public @interface JavaTag {
    String[] infos();
} 

上面注解的 infos 屬性是數組類型,因此在 Kotlin 中使用該注解時必須顯式使用 arrayOf() 函數來構建數組。例如如下代碼:

@JavaTag(infos=arrayOf("kotlin", "java"))
class Book 
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容