Kotlin知識(shí)歸納(十三) —— 注解

前序

??????注解是什么?簡(jiǎn)單說注解就是一種標(biāo)注(標(biāo)記、標(biāo)識(shí)),沒有具體的功能邏輯代碼。通過注解開發(fā)人員可以在不改變?cè)写a和邏輯的情況下在源代碼中嵌入補(bǔ)充信息。Kotlin注解的使用和Java完全一樣,聲明注解類的語法略有不同。Java 注解與 Kotlin 100% 兼容。

注解的定義

??????注解可以把額外的元數(shù)據(jù)關(guān)聯(lián)到一個(gè)聲明上,然后元數(shù)據(jù)可以被反射機(jī)制或相關(guān)的源代碼工具訪問。

聲明Kotlin的注解

??????Kotlin的聲明注解的語法和常規(guī)類的聲明非常相似,但需要在class關(guān)鍵字之前加上annotation修飾符。但Kotlin編譯器禁止為注解類指定類主體,因?yàn)樽⒔忸愔皇怯脕矶x關(guān)聯(lián)到 聲明 和 表達(dá)式 的元數(shù)據(jù)的結(jié)構(gòu)。

#daqiKotlin.kt
annotation class daqiAnnotation

Java注解聲明:

#daqiJava.java
public @interface daqiAnnotation {
}

注解的構(gòu)造函數(shù)

注解可以有接受參數(shù)的構(gòu)造函數(shù)。

其中注解的構(gòu)造函數(shù)允許的參數(shù)類型有:

  • 對(duì)應(yīng)于 Java 原生類型的類型(Int、 Long等)
  • 字符串
  • 類(Foo::class)
  • 枚舉
  • 其他注解
  • 上面已列類型的數(shù)組。

注解作為注解構(gòu)造函數(shù)的參數(shù)

當(dāng)注解作為另一個(gè)注解的參數(shù),則其名稱不用以 @ 字符為前綴:

annotation class daqiAnnotation(val str: String)

annotation class daqiAnnotation2(
    val message: String,
    val annotation: daqiAnnotation = daqiAnnotation(""))

類作為注解構(gòu)造函數(shù)的參數(shù)

當(dāng)需要將一個(gè)類指定為注解的參數(shù),請(qǐng)使用 Kotlin 類 (KClass)。Kotlin 編譯器會(huì)自動(dòng)將其轉(zhuǎn)換為 Java 類,以便 Java 代碼能夠正??吹皆撟⒔饧皡?shù) 。

annotation class daqiAnnotation(val arg1: KClass<*>, val arg2: KClass<out Any>)

@daqiAnnotation(String::class, Int::class) class MyClass

將其反編譯后,可以看到轉(zhuǎn)換為相應(yīng)的Java類:

@daqiAnnotation(
   arg1 = String.class,
   arg2 = int.class
)
public final class MyClass {
}

注意:注解參數(shù)不能有可空類型,因?yàn)?JVM 不支持將 null 作為注解屬性的值存儲(chǔ)。

Kotlin的元注解

??????和Java一樣,Kotlin的注解類也使用元注解進(jìn)行注解。用于其他注解的注解稱為元注解,可以理解為最基本的標(biāo)注。

image

??????Kotlin標(biāo)準(zhǔn)庫中定義了4個(gè)元注解,分別是:MustBeDocumented、Repeatable、Retention、Target

@Target

@Target用于指定可以應(yīng)用該注解的元素類型(類、函數(shù)、屬性、表達(dá)式等)。

查看Target的源碼:

#Annotation.kt
@Target(AnnotationTarget.ANNOTATION_CLASS)
@MustBeDocumented
public annotation class Target(vararg val allowedTargets: AnnotationTarget)

@Target注解中可以同時(shí)接收一個(gè)或多個(gè)AnnotationTarget枚舉值:

public enum class AnnotationTarget {
    //作用于類(包括枚舉類)、接口、object對(duì)象和注解類
    CLASS,
    //僅作用于注解類
    ANNOTATION_CLASS,
    //作用于泛型類型參數(shù)(暫時(shí)不支持)(JDK8)
    TYPE_PARAMETER,
    //作用于屬性
    PROPERTY,
    //作用于字段(包括枚舉常量和支持字段)。
    FIELD,
    //作用于局部變量
    LOCAL_VARIABLE,
    //作用于函數(shù)或構(gòu)造函數(shù)的參數(shù)
    VALUE_PARAMETER,
    //作用于構(gòu)造函數(shù)(包括主構(gòu)造函數(shù)和次構(gòu)造函數(shù))
    CONSTRUCTOR,
    //作用于方法(不包括構(gòu)造函數(shù))
    FUNCTION,
    //僅作用于屬性的getter函數(shù)
    PROPERTY_GETTER,
    //僅作用于屬性的setter函數(shù)
    PROPERTY_SETTER,
    //作用于類型(如方法內(nèi)參數(shù)的類型)
    TYPE,
    //作用于表達(dá)式
    EXPRESSION,
    //作用于文件,可配合 file點(diǎn)目標(biāo) 使用: (例如: @file:JvmName("daqiKotlin"))
    FILE,
    //作用于類型別名
    @SinceKotlin("1.1")
    TYPEALIAS
}

?????? 注意:Java代碼中無法使用TargetAnnotationTarget.PROPERTY的注解。如果想讓這樣的注解在Java中使用,可以添加多一條AnnotationTarget.FIELD的注解。

@Retention

@Retention 聲明注解的保留策略。

查看Retention的源碼:

@Target(AnnotationTarget.ANNOTATION_CLASS)
public annotation class Retention(val value: AnnotationRetention = AnnotationRetention.RUNTIME)

@Retention注解中可以接收一個(gè)AnnotationRetention枚舉值:

public enum class AnnotationRetention {
    //表示注解僅保留在源代碼中,編譯器將丟棄該注解。
    SOURCE,
    //注解將由編譯器記錄在class文件中 但在運(yùn)行時(shí)不需要由JVM保留。
    BINARY,
    //注解將由編譯器記錄在class文件中,并在運(yùn)行時(shí)由JVM保留,因此可以反射性地讀取它們。(默認(rèn)行為)
    RUNTIME
}

?????? 注意:Java的元注解默認(rèn)會(huì)在.class文件中保留注解,但不會(huì)讓它們?cè)谶\(yùn)行時(shí)被訪問到。大多數(shù)注解需要在運(yùn)行時(shí)存在,以至于Kotlin將RUNTIME作為@Retention注解的默認(rèn)值。

@Repeatable

允許在單個(gè)元素上多次使用相同的該注解;

查看Repeatable的源碼:

@Target(AnnotationTarget.ANNOTATION_CLASS)
public annotation class Repeatable

?????? 注意:在"嘗試使用"@Repeatable時(shí)發(fā)現(xiàn),該注解必須要在Retention元注解指定為AnnotationRetention.SOURCE時(shí)才能重復(fù)使用,但Java的@Repeatable元注解并沒有該限制。(具體的Java @Repeatable元注解的使用示例可以看這篇文章)。因?yàn)?code>@Repeatable是Java 8引入的新的元注解,而兼容Java 6的Kotlin對(duì)此有點(diǎn)不兼容?

@MustBeDocumented

指定該注解是公有 API 的一部分,并且應(yīng)該包含在生成的 API 文檔中顯示的類或方法的簽名中。

查看MustBeDocumented的源碼:

@Target(AnnotationTarget.ANNOTATION_CLASS)
public annotation class MustBeDocumented

消失的@Inherited元注解

??????相對(duì)Java的5個(gè)元注解,Kotlin只提供了與其對(duì)應(yīng)的4個(gè)元注解,Kotlin暫時(shí)不需要支持@Inherited元注解。

??????@Inherited注解表明注解類型可以從超類繼承。具體意思是:存在一個(gè)帶@Inherited元注解的注解類型,當(dāng)用戶在某個(gè)類中查詢?cè)撟⒔忸愋筒⑶覜]有此類型的注解時(shí),將嘗試從該類的超類以獲取注解類型。重復(fù)此過程,直到找到此類型的注解,或者到達(dá)類層次結(jié)構(gòu)(對(duì)象)的頂部為止。如果沒有超類具有此類型的注解,則查詢將指示相關(guān)類沒有此類注解。此注解僅適用于類聲明。

Kotlin預(yù)定義的注解

??????Kotlin為了與Java具有良好的互通性,定義了一系列注解用于攜帶一些額外信息,以便編譯器做兼容轉(zhuǎn)換。

image

@JvmDefault

將Kotlin接口的默認(rèn)方法生成Java 8的默認(rèn)方法的字節(jié)碼

查看源碼:

@SinceKotlin("1.2")
@RequireKotlin("1.2.40", versionKind = RequireKotlinVersionKind.COMPILER_VERSION)
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY)
annotation class JvmDefault

??????前面接口和類中提到,當(dāng)在Kotlin中聲明一個(gè)帶默認(rèn)方法的接口時(shí),往往會(huì)將這些“默認(rèn)方法”聲明為抽象方法,同時(shí)也會(huì)在該接口中生成一個(gè)DefaultImpls靜態(tài)內(nèi)部類,并在其中定義同名的靜態(tài)方法來提供默認(rèn)實(shí)現(xiàn)。

??????但這樣會(huì)存在一個(gè)問題,當(dāng)對(duì)舊的Kotlin接口添加新的默認(rèn)方法時(shí),實(shí)現(xiàn)該接口的Java類需要重新實(shí)現(xiàn)新添的接口方法,否則會(huì)編譯不通過。同是默認(rèn)方法,但與Java 8引入默認(rèn)方法的初衷相違背。為此,Kotlin提供了@JvmDefault注解。對(duì)標(biāo)有@JvmDefault注解的默認(rèn)方法,編譯器會(huì)將其編譯為Java 8的默認(rèn)接口。

#daqiKotlin.kt
public interface daqiInterface{
    @JvmDefault//剛添加會(huì)報(bào)錯(cuò)
    fun daqiFunc() = println("帶@JvmDefault的默認(rèn)方法")

    fun daqiFunc2() = println("默認(rèn)方法")
}
#java文件
public interface daqiInterface {
   @JvmDefault
   default void daqiFunc() {
      String var1 = "帶@JvmDefault的默認(rèn)方法";
      System.out.println(var1);
   }

   void daqiFunc2();

   public static final class DefaultImpls {
      public static void daqiFunc2(daqiInterface $this) {
         String var1 = "默認(rèn)方法";
         System.out.println(var1);
      }
   }
}

??????當(dāng)你直接添加 @JvmDefault時(shí),編譯器會(huì)報(bào)錯(cuò)。這時(shí)你需要在Gradle中配置以下參數(shù):(具體Kotlin使用Gradle看官網(wǎng))

tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs = ['-Xjvm-default = compatibility']
        //freeCompilerArgs = ['-Xjvm-default = enable']
    }
}

??????通過@JvmDefault的注解得知,配置時(shí)可以選擇-Xjvm-default = enable-Xjvm-default = compatibility。這兩個(gè)的區(qū)別是:

  • -Xjvm-default = enable會(huì)從DefaultImpls靜態(tài)內(nèi)部類中刪除對(duì)應(yīng)的方法。
  • -Xjvm-default = compatibility仍會(huì)在DefaultImpls靜態(tài)內(nèi)部類中保留對(duì)應(yīng)的方法,提高兼容性。

注意:只有JVM目標(biāo)字節(jié)碼版本1.8(-jvm-target 1.8)或更高版本才能生成默認(rèn)方法。

@JvmField

指示Kotlin編譯器不為此屬性生成getter / setter并將其修飾為public。

查看源碼:

@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
public actual annotation class JvmField

??????Kotlin聲明的屬性都默認(rèn)使用private修飾,并提供setter / getter訪問器對(duì)其進(jìn)行訪問。而@JvmField就是告訴編譯器不要為該屬性自動(dòng)創(chuàng)建setter / getter訪問器,并將對(duì)其使用public修飾。(用在伴生對(duì)象的屬性上,可生成public修飾的static屬性)

#daqiKotlin.kt
class Person{
    @JvmField
    val name:String = ""
}

反編譯后查看源碼中只聲明了一個(gè)public對(duì)象:

#java文件
public final class Person {
   @JvmField
   @NotNull
   public final String name = "";
}

?????? 注意該注解只能用在有幕后字段的屬性上,對(duì)于沒有幕后字段的屬性(例如:擴(kuò)展屬性、委托屬性等)不能使用。因?yàn)橹挥袚碛心缓笞侄蔚膶傩赞D(zhuǎn)換成Java代碼時(shí),才有對(duì)應(yīng)的Java變量。

Kotlin屬性擁有幕后字段需要滿足以下條件之一:

  • 使用默認(rèn) getter / setter 的屬性,一定有幕后字段。對(duì)于 var 屬性來說,只要 getter / setter 中有一個(gè)使用默認(rèn)實(shí)現(xiàn),就會(huì)生成幕后字段。
  • 在自定義 getter / setter 中使用了 field 的屬性。

@JvmName

指定生成Java類的類名或方法名。

查看源碼:

@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.FILE)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
public actual annotation class JvmName(actual val name: String)

??????根據(jù)注解的聲明,屬性的訪問器getter / setter也可以使用該注解,但屬性不能使用~。

??????在daqiKotlin.kt文件中聲明的所有函數(shù)和屬性(包括擴(kuò)展函數(shù))都被編譯為名為在DaqiKotlinKt的Java類的靜態(tài)方法。其中文件名首字母會(huì)被改為大寫,后置Kt。當(dāng)需要修改該Kotlin文件生成的Java類名稱時(shí),可以使用@JvmName名指定生成特定的文件名:

@file:JvmName("daqiKotlin")

package com.daqi.test

@JvmName("daqiStateFunc")
public fun daqiFunc(){

}

??????反編譯可以看到生成的Java類名稱已經(jīng)修改為daqiKotlin,而非DaqiKotlinKt,同時(shí)頂層函數(shù)daqiFunc的方法名被修改為daqiStateFunc

public final class DaqiKotlinKt {
   @JvmName(name = "daqiStateFunc")
   public static final void daqiStateFunc() {
   }
}

@JvmMultifileClass

指示Kotlin編譯器生成一個(gè)多文件的類。該文件具有在此文件中聲明的頂級(jí)函數(shù)和屬性。

查看源碼:

@Target(AnnotationTarget.FILE)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
public actual annotation class JvmMultifileClass

??????當(dāng)需要將多個(gè)Kotlin文件中的方法和屬性歸到一個(gè)Java類時(shí),可以在多個(gè)文件中聲明一樣的@JvmName,并在其下面添加@JvmMultifileClass注解。(多個(gè)文件中聲明一樣的@JvmName,但不添加@JvmMultifileClass注解會(huì)編譯不通過)

#daqiKotlin.kt
@file:JvmName("daqiKotlin")
@file:JvmMultifileClass

package com.daqi.test

fun daqi(){

}
#daqiKotlin2.kt
@file:JvmName("daqiKotlin")
@file:JvmMultifileClass

package com.daqi.test

fun daqi2(){

}

??????Kotlin編譯器會(huì)將該兩個(gè)文件中的方法和屬性合并到@JvmName注解生成的指定名稱的Java類中:

image

@JvmOverloads

指示Kotlin編譯器為此函數(shù)生成替換默認(rèn)參數(shù)值的重載函數(shù)(從最后一個(gè)開始省略每個(gè)參數(shù))。

查看源碼:

@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CONSTRUCTOR)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
public actual annotation class JvmOverloads

??????Java并沒有參數(shù)默認(rèn)值的概念,當(dāng)你從Java中調(diào)用Kotlin的默認(rèn)參數(shù)函數(shù)時(shí),必須顯示地指定所有參數(shù)值。使用@JvmOverloads注解該方法,Kotlin編譯器會(huì)生成相應(yīng)的Java重載函數(shù),從最后一個(gè)參數(shù)開始省略每個(gè)函數(shù)。

#daqiKotlin.kt
@JvmOverloads
fun daqi(name :String = "daqi",age :Int = 2019){
    println("name = $name,age = $age ")
}
image

@JvmStatic

將對(duì)象聲明或伴生對(duì)象的方法或?qū)傩缘脑L問器暴露成一個(gè)同名的Java靜態(tài)方法。

查看源碼:

@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
public actual annotation class JvmStatic

??????對(duì)于Kotlin的對(duì)象聲明和伴生對(duì)象,在Kotlin中可以像靜態(tài)函數(shù)那樣調(diào)用類名.方法名進(jìn)行調(diào)用。但在Java中,需要在這其中添加多一個(gè)CompanionINSTANCE,使調(diào)用很不自然。使用@JvmStatic注解標(biāo)記伴生對(duì)象或?qū)ο舐暶髦械姆椒ê蛯傩?,使其在Java中可以像Kotlin一樣調(diào)用這些方法和屬性。

在Kotlin中定義一個(gè)伴生對(duì)象,并用標(biāo)記@JvmStatic注解:

class daqi{
    companion object {
        @JvmStatic
        val name:String = ""

        @JvmStatic
        fun daqiFunc(){

        }
    }
}

??????反編譯可以觀察到 伴生對(duì)象類 或 對(duì)象聲明類 中聲明了屬于它們自己的方法和屬性,但同時(shí)在對(duì)象聲明類本身或伴生對(duì)象類的外部類中也聲明了一樣的靜態(tài)的方法和屬性訪問器供外部直接訪問。

public final class daqi {
   @NotNull
   private static final String name = "";
   public static final daqi.Companion Companion = new daqi.Companion((DefaultConstructorMarker)null);

   @NotNull
   public static final String getName() {
      daqi.Companion var10000 = Companion;
      return name;
   }

   @JvmStatic
   public static final void daqiFunc() {
      Companion.daqiFunc();
   }

   public static final class Companion {
      @JvmStatic
      public static void name$annotations() {
      }

      @NotNull
      public final String getName() {
         return daqi.name;
      }

      @JvmStatic
      public final void daqiFunc() {
      }
   }
}

??????所以,如果對(duì)象聲明和伴生對(duì)象需要和Java層進(jìn)行比較頻繁的交互時(shí),建議還是加上@JvmStatic

@JvmSuppressWildcards 和 @JvmWildcard

@JvmSuppressWildcards指示編譯器為泛型參數(shù)生成或省略通配符。(默認(rèn)是省略)
@JvmWildcard指示編譯器為為泛型參數(shù)生成通配符。

查看源碼:

@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY, AnnotationTarget.TYPE)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
public actual annotation class JvmSuppressWildcards(actual val suppress: Boolean = true)

--------------------------------------------------------------------------

@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
public actual annotation class JvmWildcard

@PurelyImplements

指示Kotlin編譯器將帶該注釋的Java類視為給定Kotlin接口的純實(shí)現(xiàn)?!癙ure”在這里表示類的每個(gè)類型參數(shù)都成為該接口的非平臺(tái)類型參數(shù)。

查看源碼:

@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
public annotation class PurelyImplements(val value: String)

??????Kotlin對(duì)來自Java的變量會(huì)當(dāng)作平臺(tái)類型來處理,由開發(fā)者覺得其是可空還是非空。但即便將其聲明為非空,但其實(shí)他還是能接收空值或者返回空值。

#java文件
class MyList<T> extends AbstractList<T> { ... }
#kotlin文件
MyList<Int>().add(null) // 編譯通過

??????但可以借助@PurelyImplements注解,并攜帶對(duì)應(yīng)的Kotlin接口。使其與Kotlin接口對(duì)應(yīng)的類型參數(shù)不被當(dāng)作平臺(tái)類型來處理。

#java文件
@PurelyImplements("kotlin.collections.MutableList")
class MyPureList<T> extends AbstractList<T> { ... }
MyPureList<Int>().add(null) // 編譯不通過
MyPureList<Int?>().add(null) // 編譯通過

@Throws

等價(jià)于Java的throws關(guān)鍵字

查看源碼:

@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.CONSTRUCTOR)
@Retention(AnnotationRetention.SOURCE)
public annotation class Throws(vararg val exceptionClasses: KClass<out Throwable>)

例子

@Throws(IOException::class)
fun daqi() {
    
}

@Strictfp

等價(jià)于Java的strictfp關(guān)鍵字

查看源碼:

@Target(FUNCTION, CONSTRUCTOR, PROPERTY_GETTER, PROPERTY_SETTER, CLASS)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
public actual annotation class Strictfp

@Transient

等價(jià)于Java的transient關(guān)鍵字

查看源碼:

@Target(FIELD)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
public actual annotation class Transient

@Synchronized

等價(jià)于Java的synchronized關(guān)鍵字

查看源碼:

@Target(FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
public actual annotation class Synchronized

@Volatile

等價(jià)于Java的volatile關(guān)鍵字

查看源碼:

@Target(FIELD)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
public actual annotation class Volatile

點(diǎn)目標(biāo)聲明

??????許多情況下, Kotlin代碼中的單個(gè)聲明會(huì)對(duì)應(yīng)成多個(gè) Java 聲明 ,而且它們每
個(gè)都能攜帶注解。例如, Kotlin 屬性就對(duì)應(yīng)了 Java 宇段、 getter ,以
及一個(gè)潛在的 setter。這時(shí)需要使用點(diǎn)目標(biāo)指定說明注解用在什么地方。

點(diǎn)目標(biāo)聲明被用來說明要注解的元素。使用點(diǎn)目標(biāo)被放在@符號(hào)和注解名
稱之間,并用冒號(hào)和注解名稱隔開。

點(diǎn)目標(biāo)的完整列表如下:

  • property————Java 的注解不能應(yīng)用這種使用點(diǎn)目標(biāo)
  • field————為屬性生成的字段
  • get ————屬性的 getter
  • set ————屬性的 setter
  • receiver ————擴(kuò)展函數(shù)或者擴(kuò)展屬性的接收者參數(shù)。
  • param————構(gòu)造方法的參數(shù)。
  • setparam————屬性 setter 的參數(shù)
  • delegate ————為委托屬性存儲(chǔ)委托實(shí)例的字段
  • file ———— 包含在文件中聲明的頂層函數(shù)和屬性的類。
//注解的是get方法,而不是屬性
@get:daqiAnnotation
val daqi:String = ""

@Target(AnnotationTarget.PROPERTY_GETTER)
annotation class daqiAnnotation()

參考資料:

android Kotlin系列:

Kotlin知識(shí)歸納(一) —— 基礎(chǔ)語法

Kotlin知識(shí)歸納(二) —— 讓函數(shù)更好調(diào)用

Kotlin知識(shí)歸納(三) —— 頂層成員與擴(kuò)展

Kotlin知識(shí)歸納(四) —— 接口和類

Kotlin知識(shí)歸納(五) —— Lambda

Kotlin知識(shí)歸納(六) —— 類型系統(tǒng)

Kotlin知識(shí)歸納(七) —— 集合

Kotlin知識(shí)歸納(八) —— 序列

Kotlin知識(shí)歸納(九) —— 約定

Kotlin知識(shí)歸納(十) —— 委托

Kotlin知識(shí)歸納(十一) —— 高階函數(shù)

Kotlin知識(shí)歸納(十二) —— 泛型

Kotlin知識(shí)歸納(十三) —— 注解

Kotlin知識(shí)歸納(十四) —— 反射

image
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容