最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Kotlin注解與反射的定義及創(chuàng)建使用詳解

 更新時間:2022年12月08日 11:36:27   作者:無糖可樂愛好者  
這篇文章主要為大家介紹了Kotlin注解與反射的定義及使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

1.注解

1.定義

注解是將元數(shù)據(jù)附加到代碼的地方。從字面意思理解它就是對知識點的補充,一種描述。在Java中最常見的注解就是@Override或者就是Retrofit中的@GET、@POST等。

2.注解的創(chuàng)建

創(chuàng)建時用annotation修飾符進行聲明,如

annotation class GET()

這樣就創(chuàng)建好了一個注解,但是這里想要完全使用還要添加一些屬性:

  • @Target:指定可以用該注解標(biāo)注的元素的可能的類型(類、函數(shù)、屬性、表達式);
  • @Retention:指定該注解是否存儲在編譯后的class文件中以及它在運行時能否通過反射可見,默認(rèn)為true;
  • @Repeatable:允許在單個元素上多次使用相同的該注解;
  • @MustBeDocumented:指定該注解是公有API的一部分,并且應(yīng)該包含在生成的API文檔中顯示的類或方法的簽名中,一般用于SDK文檔中。

這里重點要注意的是 @Target和@Retention

//@Target
public enum class AnnotationTarget {
    // 類、接口、object、注解類
    CLASS,
    // 注解類
    ANNOTATION_CLASS,
    // 泛型參數(shù)
    TYPE_PARAMETER,
    // 屬性
    PROPERTY,
    // 字段、幕后字段
    FIELD,
    // 局部變量
    LOCAL_VARIABLE,
    // 函數(shù)參數(shù)
    VALUE_PARAMETER,
    // 構(gòu)造器
    CONSTRUCTOR,
    // 函數(shù)
    FUNCTION,
    // 屬性的getter
    PROPERTY_GETTER,
    // 屬性的setter
    PROPERTY_SETTER,
    // 類型
    TYPE,
    // 表達式
    EXPRESSION,
    // 文件
    FILE,
    // 類型別名
    TYPEALIAS
}
//@Retention
public enum class AnnotationRetention {
    // 注解只存在于源代碼,編譯后不可見
    SOURCE,
    // 注解編譯后可見,運行時不可見
    BINARY,
    // 編譯后可見,運行時可見	默認(rèn)
    RUNTIME
}

3.注解的使用

@Target(AnnotationTarget.FUNCTION)			//注解用于方法
@Retention(AnnotationRetention.RUNTIME)		//運行時可見,編譯時可見
annotation class Custom()
//正常使用不報錯
@Custom
fun test() {
    println("")
}
//報錯,因為注解不支持Class,如果要支持就需要在@Target里面加上AnnotationTarget.CLASS
@Custom
class Test{
}

上面的代碼是一個自定義且最簡單的一個用法,現(xiàn)在看一下Kotlin中自帶的一個注解,這個注解用來標(biāo)注廢棄的方法或者類等定義,比較常見

@Target(CLASS, FUNCTION, PROPERTY, ANNOTATION_CLASS, CONSTRUCTOR, PROPERTY_SETTER, PROPERTY_GETTER, TYPEALIAS)
@MustBeDocumented
public annotation class Deprecated(
    val message: String,
    val replaceWith: ReplaceWith = ReplaceWith(""),
    val level: DeprecationLevel = DeprecationLevel.WARNING
)

@Tageget:支持類、 函數(shù)、 屬性、注解類、構(gòu)造器、屬性 getter、屬性 setter、類型別名

Deprecated內(nèi)部還傳遞了幾個參數(shù):

  • message:對廢棄內(nèi)容的提示信息
  • repleaceWith:表示用什么內(nèi)容來替代被廢棄的內(nèi)容。需要注意的是后面的ReplaceWith也是一個注解,也就是說Kotlin中注解中是可以添加注解的,只不過添加時不可以用@
  • level:警告程度,有WARNING、ERROR、HIDDEN

注解中允許的參數(shù)有:

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

注解參數(shù)不能有可空類型,因為 JVM 不支持將 null 作為注解屬性的值存儲。

2.反射

1.定義

反射是指計算機程序在運行時(runtime)可以訪問、檢測和修改它本身狀態(tài)或行為的一種能力。用比喻來說,反射就是程序在運行的時候能夠“觀察”并且修改自己的行為。

反射在業(yè)務(wù)開發(fā)中用的較少,主要是在架構(gòu)設(shè)計中,可以極大地提升架構(gòu)的靈活性。

Kotlin的反射具備三個特點:

  • 感知程序的狀態(tài),包含程序的運行狀態(tài)以及源代碼結(jié)構(gòu);
  • 修改程序的狀態(tài);
  • 根據(jù)程序的狀態(tài),調(diào)整自身的決策行為。

2.反射的應(yīng)用

首先要加入一個依賴才可以使用反射

implementation "org.jetbrains.kotlin:kotlin-reflect"

然后根據(jù)上面提到的三個特點進行講解:

  • 感知程序的狀態(tài):

舉例:假設(shè)現(xiàn)在有兩個對象,在不傳遞具體對象的前提下想要打印出他們每一個屬性的名稱以及具體的值

fun main() {
    val person = Person("張三", 22)
    val animal = Animal("貓", "用腳走", "貓糧")
    findClassAttribute(person)
    findClassAttribute(animal)
}
fun findClassAttribute(obj: Any) {
}
data class Person(val name: String, var age: Int)
data class Animal(var species: String, val walkWay: String, var food: String)
//期望結(jié)果:
//Person.name = 張三
//Person.age = 22
//Animal.species = 貓
//Animal.walkWay = 用腳走
//Animal.food = 貓糧

上面只是定義了兩個類,具體項目中可能會很有很多的類,因此用when的方式是行不通的因為這樣工作量還是比較大的,那么用反射反而是一個比較好的方式,那要如何實現(xiàn)?

fun findClassAttribute(obj: Any) {
    obj::class.memberProperties.forEach {
        println("${obj::class.simpleName}.${it.name} = ${it.getter.call(obj)}")
    }
}
//輸出結(jié)果
//Person.age = 22
//Person.name = 張三
//Animal.food = 貓糧
//Animal.species = 貓
//Animal.walkWay = 用腳走

看到這個是不是一臉懵?這是啥鬼東西。我們對上面的代碼進行分析就明白了:

  • obj::class: 這是類引用,是Kotlin的反射語法,通過這樣的語法可以獲取變量的類型信息并且可以拿到這個變量的類型KClass,也就是我們的PersonAnimal。
  • memberProperties: 通過前面的obj::class拿到了具體的類型,那么也就拿到了這個這個類型的所有信息,比如說simpleName、constructors,而memberProperties就是獲取類的成員屬性,然后通過foreach遍歷出來就好了。
  • it:KProperty1: 這里的KProperty1是KClass的子類,通過it.name拿到屬性的命名,it.getter.call拿到屬性的值。

經(jīng)過上述幾個關(guān)鍵信息就獲取到了我們想要的輸出結(jié)果,這就是感知程序的狀態(tài)。

  • 修改程序狀態(tài)

拿到每個屬性的命名和值之后我想要修改動物類的某個屬性的值怎么辦?增加一個changeClassAttributeValue方法用來修改屬性值

fun changeClassAttributeValue(obj: Any) {
    obj::class.memberProperties.forEach {
        if (it.name == "food"                                   //判斷要修改的屬性名是【food】
            && it is KMutableProperty1                          //判斷這個屬性是否可以被修改,val屬性不可被修改,var屬性可以
            && it.setter.parameters.size == 2                   //修改屬性需要setter,我們要先判斷 setter 的參數(shù)是否符合預(yù)期,這里 setter 的參數(shù)個數(shù)應(yīng)該是 2,第一個參數(shù)是 obj 自身,第二個是實際的值
            && it.getter.returnType.classifier == String::class //判斷要修改的屬性是不是string類型
        ) {
            it.setter.call(obj, "雞胸肉")						 //修改屬性值
            println("========屬性值修改========")
        }
    }
}
fun main() {
    val person = Person("張三", 22)
    val animal = Animal("貓", "用腳走", "貓糧")
    changeClassAttributeValue(animal)
    findClassAttribute(animal)
}
//輸出結(jié)果
//Animal.food = 雞胸肉
//Animal.species = 貓
//Animal.walkWay = 用腳走

根據(jù)屬性值food貓糧修改為雞胸肉。 這種操作方式就是反射的第二個特點:修改程序的狀態(tài)

  • 根據(jù)程序的狀態(tài),調(diào)整自身的決策行為。

上面我們已經(jīng)調(diào)整狀態(tài)了,那么我還想加一個修改屬性:species,吃雞胸肉的也可以是小狗,因此我們只需要再加一個else即可,這樣就實現(xiàn)了最后一個特點:根據(jù)程序的狀態(tài),調(diào)整自身的決策行為。

fun changeClassAttributeValue(obj: Any) {
    obj::class.memberProperties.forEach {
        if (it.name == "food"                                   //判斷要修改的屬性名是【food】
            && it is KMutableProperty1                          //判斷這個屬性是否可以被修改
            && it.setter.parameters.size == 2                   //修改屬性需要setter,我們要先判斷 setter 的參數(shù)是否符合預(yù)期,這里 setter 的參數(shù)個數(shù)應(yīng)該是 2,第一個參數(shù)是 obj 自身,第二個是實際的值
            && it.getter.returnType.classifier == String::class //判斷要修改的屬性是不是string類型
        ) {
            it.setter.call(obj, "雞胸肉")
            println("======== food 屬性值修改========")
        } else if (it.name == "species"                         //判斷要修改的屬性名是【species】
            && it is KMutableProperty1                          //判斷這個屬性是否可以被修改,val屬性不可被修改,var屬性可以
            && it.setter.parameters.size == 2                   //修改屬性需要setter,我們要先判斷 setter 的參數(shù)是否符合預(yù)期,這里 setter 的參數(shù)個數(shù)應(yīng)該是 2,第一個參數(shù)是 obj 自身,第二個是實際的值
            && it.getter.returnType.classifier == String::class //判斷要修改的屬性是不是string類型
        ) {
            it.setter.call(obj, "小狗")
            println("======== species 屬性值修改========")
        } else { // 差別在這里
            println("沒找到相關(guān)屬性")
        }
    }
}
fun main() {
    val person = Person("張三", 22)
    val animal = Animal("貓", "用腳走", "貓糧")
    changeClassAttributeValue(animal)
    findClassAttribute(animal)
}
//輸出結(jié)果
//Animal.food = 雞胸肉
//Animal.species = 小狗
//Animal.walkWay = 用腳走

這里還要說明的是可修改的屬性值一定是用var修飾的,如果在demo過程中出現(xiàn)不能修改的要檢查屬性聲的明是否可修改。

上面的代碼通過memberProperties進入之后可以發(fā)現(xiàn)它用到了 Kotlin 反射的幾個關(guān)鍵類:KClass、KCallable、KParameter、KType?,F(xiàn)在,我們來進一步看看它們的關(guān)鍵成員

KClass 代表了一個 Kotlin 的類,下面是它的重要成員:

  • simpleName: 獲取類名稱,如果是匿名內(nèi)部類獲取的值為null;
  • qualifiedName: 完整的類名。用【.】分隔
  • members: 可訪問的所有函數(shù)和屬性,類型為Collection<KCallable<*>>
  • constructors: 獲取所有構(gòu)造函數(shù),類型為Collection<KFunction<T>>;
  • nestedClasses: 獲取聲明的所有類包含內(nèi)部嵌套類和嵌套靜態(tài)類,類型為Collection<KClass<*>>;
  • objectInstance: 對象聲明的實例如果這個類不是對象聲明則返回null;
  • typeParameters: 獲取參數(shù)類型列表,但不包括外部類的參數(shù)類型,類型為List<KTypeParameter>
  • supertypes: 該類的直接超類型列表,按它們在源代碼中列出的順序排列,類型為List<KType>;
  • sealedSubclasses: 如果是密封類則直接獲取子類的列表否則為空,類型為List<KClass<out T>>;
  • visibility: 類的可見性,如果可見性不能在Kotlin中表示則為null;
  • isFinal: 返回該類是否是final類型,類型為Boolean,如果是則為true;
  • isOpen: 返回該類是否是open修飾的,類型為Boolean,如果是則為true;
  • isAbstract: 返回該類是否是abstract的,類型為Boolean,如果是則為true;
  • isSealed: 返回該類是否是密封類,類型為Boolean,如果是則為true;
  • isData: 返回該類是否是數(shù)據(jù)類,類型為Boolean,如果是則為true;
  • isInner: 返回該類是否是內(nèi)部類,類型為Boolean,如果是則為true;
  • isCompanion: 返回該類是否是伴生對象,類型為Boolean,如果是則為true;
  • isFun: 返回該類是否是函數(shù)式接口,類型為Boolean,如果是則為true;
  • isValue: 返回該類是否是Value Class,類型為Boolean,如果是則為true。

KCallable 代表了 Kotlin 當(dāng)中的所有可調(diào)用的元素,比如函數(shù)、屬性、甚至是構(gòu)造函數(shù)。下面是 KCallable 的重要成員:

  • name: 獲取可調(diào)用的聲明的名稱,如果可調(diào)用對象沒有名稱則創(chuàng)建一個特殊的名稱,沒有名稱包括:

構(gòu)造函數(shù)名稱為"";

屬性訪問器:一個名為foo的屬性getter將會有名稱,同理setter將會有名稱

  • parameters 獲取所有可調(diào)用的參數(shù),如果需要this實例或者擴展接收方參數(shù)那么他們通過List返回,返回類型為List<KParameter>;
  • returnType 獲取返回值的類型,返回類型為KType;
  • typeParameters 獲取可調(diào)用參數(shù)的類型以列表返回,返回類型為List<KTypeParameter>;
  • visibility 元素的可見性,如果可見性不能在Kotlin中表示則為null;
  • isFinal 返回該元素是否是final修飾的,類型為Boolean,如果是則為true;
  • isOpen 返回該元素是否是open修飾的,類型為Boolean,如果是則為true;
  • isAbstract 返回該元素是否是abstract修飾的,類型為Boolean,如果是則為true;
  • isSuspend 返回該元素是否是掛起函數(shù),類型為Boolean,如果是則為true。

KParameter,代表了KCallable當(dāng)中的參數(shù),它的重要成員如下:

  • index: 參數(shù)在參數(shù)列表中的索引,從0開始;
  • name: 參數(shù)聲明的名稱,如果沒有名稱或者名稱在運行時不可用則返回null;
  • type: 參數(shù)類型,對于可變參數(shù)參數(shù)類型是數(shù)組而不是單個元素,返回類型為KType
  • kind: 參數(shù)的種類

INSTANCE: 對象的實例;

EXTENSION_RECEIVER: 擴展接收者;

VALUE: 具體的值;

  • isOptional: 如果此參數(shù)是可選的,則為true,當(dāng)通過KCallable進行調(diào)用時可以省略此參數(shù)。callBy,否則為false。參數(shù)可選的情況如下:

默認(rèn)值在該參數(shù)的聲明中提供;

形參在成員函數(shù)中聲明,并且在超函數(shù)中有一個對應(yīng)的形參是可選的。

  • isVararg: 如果參數(shù)為可變長度則返回true。

KType,代表了 Kotlin 當(dāng)中的類型,它重要的成員如下:

  • classifier: 類型對應(yīng)Kotlin類,即KClass,如果不能在Kotlin中表示則返回null;
  • arguments: 是該類型中的分類器的參數(shù)傳遞的類型參數(shù),就是泛型。
  • isMarkedNullable: 是否被標(biāo)記為可空類型,就是后面有沒有【?】。

這幾個類集合了很多個API,了解每一個的作用之后再了解反射就會很簡單了。

以上就是Kotlin注解與反射的定義及使用詳解的詳細(xì)內(nèi)容,更多關(guān)于Kotlin注解反射的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Flutter隨機迷宮生成和解迷宮小游戲功能的源碼

    Flutter隨機迷宮生成和解迷宮小游戲功能的源碼

    這篇文章主要介紹了Flutter隨機迷宮生成和解迷宮小游戲,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-04-04
  • Android打造流暢九宮格抽獎活動效果

    Android打造流暢九宮格抽獎活動效果

    抽獎活動有很多種形式,轉(zhuǎn)盤抽獎,九宮格抽獎,刮刮卡抽獎,這篇文章主要為大家詳細(xì)介紹了如何打造流暢九宮格抽獎活動效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-11-11
  • Android檢測手機中存儲卡及剩余空間大小的方法(基于Environment,StatFs及DecimalFormat)

    Android檢測手機中存儲卡及剩余空間大小的方法(基于Environment,StatFs及DecimalFormat

    這篇文章主要介紹了Android檢測手機中存儲卡及剩余空間大小的方法,基于Environment,StatFs及DecimalFormat實現(xiàn)該功能,具有一定參考借鑒價值,需要的朋友可以參考下
    2016-01-01
  • Android實現(xiàn)隨意拖動View效果的實例代碼

    Android實現(xiàn)隨意拖動View效果的實例代碼

    這篇文章主要介紹了Android實現(xiàn)隨意拖動View效果,本文通過實例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-07-07
  • Android應(yīng)用內(nèi)存泄漏優(yōu)化指南

    Android應(yīng)用內(nèi)存泄漏優(yōu)化指南

    內(nèi)存泄漏(Memory Leak)是指應(yīng)用中不再使用的對象因錯誤引用無法被垃圾回收(GC),導(dǎo)致內(nèi)存占用持續(xù)增長,最終可能引發(fā) OOM(Out Of Memory)崩潰?或 應(yīng)用卡頓,以下是 Android 內(nèi)存泄漏的優(yōu)化方案,涵蓋檢測工具、常見場景及解決方案,需要的朋友可以參考下
    2025-03-03
  • Android開發(fā)使用UncaughtExceptionHandler捕獲全局異常

    Android開發(fā)使用UncaughtExceptionHandler捕獲全局異常

    本文主要介紹在Android開發(fā)中使用UncaughtExceptionHandler捕獲全局異常,需要的朋友可以參考下。
    2016-06-06
  • Android自帶API實現(xiàn)分享功能

    Android自帶API實現(xiàn)分享功能

    這篇文章主要為大家詳細(xì)介紹了Android自帶API實現(xiàn)分享功能,實現(xiàn)文字和圖片的分享,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-04-04
  • Android使用WindowManager制作一個可拖動的控件

    Android使用WindowManager制作一個可拖動的控件

    這篇文章主要為大家詳細(xì)介紹了Android使用WindowManager制作一個可拖動的控件的相關(guān)資料,感興趣的小伙伴們可以參考一下
    2016-08-08
  • Android實現(xiàn)Tab切換界面功能詳解

    Android實現(xiàn)Tab切換界面功能詳解

    這篇文章主要為大家詳細(xì)介紹了Android如何實現(xiàn)Tab切換界面的功能,以及對Tab變化事件進行監(jiān)聽。文中示例代碼講解詳細(xì),感興趣的可以了解一下
    2022-05-05
  • Android實現(xiàn)圖片雙指縮放

    Android實現(xiàn)圖片雙指縮放

    這篇文章主要為大家詳細(xì)介紹了Android實現(xiàn)圖片雙指縮放,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-11-11

最新評論

南宫市| 哈密市| 静宁县| 沐川县| 富川| 抚宁县| 绥中县| 安庆市| 绩溪县| 丹东市| 大理市| 南部县| 长岭县| 泸西县| 神农架林区| 泗阳县| 黄龙县| 齐河县| 南昌市| 临城县| 修武县| 萍乡市| 若羌县| 广饶县| 乡宁县| 澄迈县| 柳江县| 霍邱县| 安岳县| 虹口区| 道孚县| 黄龙县| 临安市| 安阳县| 达州市| 井陉县| 临桂县| 子洲县| 武宁县| 泰安市| 临高县|