国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

第12章 元編程與注解、反射 《Kotlin 項(xiàng)目實(shí)戰(zhàn)開發(fā)》

joyqi / 2234人閱讀

摘要:第章元編程與注解反射反射是在運(yùn)行時(shí)獲取類的函數(shù)方法屬性父類接口注解元數(shù)據(jù)泛型信息等類的內(nèi)部信息的機(jī)制。本章介紹中的注解與反射編程的相關(guān)內(nèi)容。元編程本質(zhì)上是一種對(duì)源代碼本身進(jìn)行高層次抽象的編碼技術(shù)。反射是促進(jìn)元編程的一種很有價(jià)值的語言特性。

第12章 元編程與注解、反射

反射(Reflection)是在運(yùn)行時(shí)獲取類的函數(shù)(方法)、屬性、父類、接口、注解元數(shù)據(jù)、泛型信息等類的內(nèi)部信息的機(jī)制。這些信息我們稱之為 RTTI(Run-Time Type Information,運(yùn)行時(shí)類型信息) 。

注解(Annotation)是我們給代碼添加的元數(shù)據(jù)。使用注解可以寫出更加簡(jiǎn)潔干凈的代碼,同時(shí)還可以在編譯期進(jìn)行類型檢查。Kotlin 的注解完全兼容 Java 的注解。

本章介紹 Kotlin 中的注解與反射編程的相關(guān)內(nèi)容。

12.1 元編程簡(jiǎn)介

說到元編程(Meta-programming),我們從 Meta- 這個(gè)前綴開始說起。Meta- 這個(gè)前綴在在西方哲學(xué)界指的是:關(guān)于事物自身的事物。比如,心理學(xué)領(lǐng)域有一門專門研究關(guān)于人類認(rèn)知心理的學(xué)科叫認(rèn)知心理學(xué)(cognitive psychology)。而還有一門學(xué)科是研究人對(duì)自己的認(rèn)知過程的認(rèn)知,叫做元認(rèn)知心理學(xué)(Meta cognitive psychology ),又稱反省認(rèn)知、監(jiān)控認(rèn)知、超認(rèn)知、反審認(rèn)知等。元認(rèn)知的本質(zhì)是人類對(duì)自身認(rèn)知活動(dòng)的自我意識(shí)和自我調(diào)節(jié)。

再例如, meta-knowledge 就是“關(guān)于知識(shí)本身的知識(shí)”,meta-data 就是“關(guān)于數(shù)據(jù)的數(shù)據(jù)”,meta-language 就是“關(guān)于語言的語言”,而 meta-programming 也就是“關(guān)于編程的編程”, 也就是我們通常所說的“元編程”。

元編程(Meta-programming)是指用代碼在編譯期或運(yùn)行期生成或改變代碼的一種編程形式。編寫元程序的語言稱之為元語言,被操縱的語言稱之為目標(biāo)語言。如果一門語言中具備同時(shí)是元語言也是目標(biāo)語言的能力,這就是反射。

一般代碼的操作對(duì)象是數(shù)據(jù),元編程操作的對(duì)象是其他代碼。無關(guān)業(yè)務(wù)邏輯,只跟當(dāng)前代碼結(jié)構(gòu)相關(guān)的代碼。比如在Java中在運(yùn)行時(shí)通過反射把所有以*ServiceImpl 結(jié)尾的類找出來,加上log日志或者進(jìn)行監(jiān)控統(tǒng)計(jì)等其它動(dòng)作。

除非程序的運(yùn)行期的輸入數(shù)據(jù)會(huì)被直接或間接轉(zhuǎn)化成代碼,否則元編程不會(huì)給程序帶來新的邏輯。元編程本質(zhì)上是一種對(duì)源代碼本身進(jìn)行高層次抽象的編碼技術(shù)。"元編程"比"我們手寫代碼"多提供了一個(gè)抽象層次! 我們其實(shí)就是用代碼中的元數(shù)據(jù)(按照一定的協(xié)議規(guī)則來定義,也就是注解的語法規(guī)范)來進(jìn)行動(dòng)態(tài)插入新代碼邏輯,也就是用來動(dòng)態(tài)生成代碼的程序。其實(shí),根本沒有什么“元編程”,有的只是“編程”。

反射是促進(jìn)元編程的一種很有價(jià)值的語言特性。編程的語言中的泛型支持也使用元編程能力。元編程通常有兩種方式:一種是通過應(yīng)用程序接口(API)來暴露運(yùn)行時(shí)系統(tǒng)的內(nèi)部信息;另一種方法是在運(yùn)行時(shí)動(dòng)態(tài)執(zhí)行包含編程命令的字符串。因此,“程序能編寫程序”。雖然兩種方法都能用,但大多數(shù)方法主要靠其中一種。

注解是把編程中的元數(shù)據(jù)信息直接寫在源代碼中,而不是保存在外部文件中。

在使用注解之前(甚至在使用之后),XML配置文件被廣泛的應(yīng)用于編程過程中的元數(shù)據(jù)的描述。后來程序員們逐漸發(fā)現(xiàn)XML的維護(hù)越來越糟糕了,進(jìn)而希望直接使用一些和代碼緊耦合的“元數(shù)據(jù)”,而不是像 XML 那樣和代碼分離。把注解使用的淋漓盡致的 Spring Boot 框架中,基本不需要一行XML配置,幾乎全部使用注解就搞定一個(gè) Spring 企業(yè)級(jí)應(yīng)用的開發(fā)。

“XML vs. Annotation”,這其實(shí)是一個(gè) “陰陽(yáng)交融” 的編程之道,很多時(shí)候要看具體的問題場(chǎng)景來決定采用哪種方式。XML配置就是為了分離代碼和配置而引入的,而注解是為了希望使用一些和代碼緊耦合的東西。萬事萬物就是這樣的陰陽(yáng)交合辯證發(fā)展的過程。

注解是將元數(shù)據(jù)附加到代碼的方法。而反射可以在運(yùn)行時(shí)把代碼中的注解元數(shù)據(jù)獲取到,并在目標(biāo)代碼執(zhí)行之前進(jìn)行動(dòng)態(tài)代理,實(shí)現(xiàn)業(yè)務(wù)邏輯的動(dòng)態(tài)注入,這其實(shí)就是 AOP (Aspect Oriented Programming,面向切面編程(也叫面向方面)的核心思想——通過運(yùn)行期動(dòng)態(tài)代理(和預(yù)編譯方式)實(shí)現(xiàn)在不修改源代碼的情況下, 給程序動(dòng)態(tài)添加新功能的一種技術(shù)。

例如,在 Spring 、 Mybatis 、JPA 等諸多框架中的核心功能都是使用了注解與反射的技術(shù)來實(shí)現(xiàn)的。例如我們常用的 Spring 框架中的各種注解 @Repository 、@Service 、 @Transactional 、@RequestMapping 、@ResponseBody 等),Mybatis 框架中的各種注解 @Select 、 @Update 、@Param 等。

另外,需要重點(diǎn)提到的就是當(dāng)下非常流行的 Spring Boot 框架。在使用 Spring Boot 開發(fā)企業(yè)級(jí)應(yīng)用時(shí),完全不需要使用一行 XML 配置,整個(gè)的源代碼工程都能基于注解來開發(fā)(application.propertis配置文件另當(dāng)別論,更多關(guān)于SpringBoot框架開發(fā)的知識(shí),我們將在后面的章節(jié)中介紹)。

12.2 注解

Kotlin的注解跟Java注解也完全兼容。我們可以在Kotlin代碼中很自然地使用Java中的注解。也就是說,我們使用Kotlin語言集成SpringBoot框架開發(fā)的過程將會(huì)非常自然,幾乎跟使用原生Java語言開發(fā)一樣流暢,同時(shí)還能享受Kotlin語言帶來的諸多簡(jiǎn)潔優(yōu)雅同時(shí)還非常強(qiáng)大的特性。

12.2.1 聲明注解

Kotlin中聲明注解使用 annotation class 關(guān)鍵字。例如,我們聲明兩個(gè)注解Run和TestCase 如下

@Target(AnnotationTarget.CLASS,
        AnnotationTarget.FUNCTION,
        AnnotationTarget.VALUE_PARAMETER,
        AnnotationTarget.EXPRESSION)
@Retention(AnnotationRetention.RUNTIME)
@Repeatable
@MustBeDocumented
annotation class TestCase(val id: String)


@Target(AnnotationTarget.CLASS,
        AnnotationTarget.FUNCTION,
        AnnotationTarget.VALUE_PARAMETER,
        AnnotationTarget.EXPRESSION)
@Retention(AnnotationRetention.RUNTIME)
@Repeatable
@MustBeDocumented
annotation class Run

從這個(gè)關(guān)鍵字上我們就可以看出注解也是一種 class ,編譯器同樣可以對(duì)注解類型在編譯期進(jìn)行類型檢查。

我們自定義的注解上面使用的注解,我們稱之為元注解(meta-annotation)。我們通過向注解類添加元注解的方法來指定其他屬性。元注解說明如下表

元注解名稱 功能說明
@Target 指定這個(gè)注解可被用于哪些元素 ( 這些元素定義在kotlin.annotation.AnnotationTarget 枚舉類中。它們是:類 CLASS, 注解類 ANNOTATION_CLASS,泛型參數(shù) TYPE_PARAMETER,函數(shù) FUNCTION, 屬性 PROPERTY, 用于描述域成員變量的 FIELD,局部變量 LOCAL_VARIABLE,VALUE_PARAMETER,CONSTRUCTOR,PROPERTY_GETTER,PROPERTY_SETTER, 用于描述類、接口(包括注解類型) 或enum聲明的 TYPE, 表達(dá)式 EXPRESSION,文件 FILE,類型別名TYPEALIAS等。
@Retention 指定這個(gè)注解的信息是否被保存到編譯后的 class 文件中, 以及在運(yùn)行時(shí)是否可以通過反射訪問到它, 可取的枚舉值有3個(gè),分別是: SOURCE (注解數(shù)據(jù)不存儲(chǔ)在二進(jìn)制輸出),BINARY(注解數(shù)據(jù)存儲(chǔ)在二進(jìn)制輸出中, 但反射不可見), RUNTIME(注解數(shù)據(jù)存儲(chǔ)在二進(jìn)制輸出中, 可用于反射 (默認(rèn)值 ) 。
@Repeatable 允許在單個(gè)元素上多次使用同一個(gè)注解
@MustBeDocumented 表示這個(gè)注解是公開 API 的一部分, 在自動(dòng)產(chǎn)生的 API 文檔的類或者函數(shù)簽名中, 應(yīng)該包含這個(gè)注解的信息。
12.2.2 使用注解

上面我們聲明了Run注解,它可以使用在CLASS,F(xiàn)UNCTION,VALUE_PARAMETER,EXPRESSION上。我們這里給出的示例是用在類上

@Run
class SwordTest {}

我們聲明的 TestCase 注解,它有個(gè)構(gòu)造函數(shù),傳入的參數(shù)是一個(gè)String類型的id。我們把這個(gè)注解用在函數(shù)上

@Run
class SwordTest {
    @TestCase(id = "1")
    fun testCase(testId: String) {
        println("Run SwordTest ID = ${testId}")
    }
}

上面是注解在代碼中的簡(jiǎn)單使用。其中的 @TestCase(id = "1") 是注解的構(gòu)造函數(shù)的使用。注解可以有帶參數(shù)的構(gòu)造器。注解參數(shù)的可支持?jǐn)?shù)據(jù)類型如下:

所有基本數(shù)據(jù)類型(Int,Float,Boolean,Byte,Double,Char,Long,Short)

String 類型

KClass 類型

enum 類型

Annotation 類型

以上所有引用類型的數(shù)組(注意,不包括基本數(shù)據(jù)類型)

例如下面的都是合法的注解構(gòu)造函數(shù)的參數(shù)類型

annotation class TestCase(val id: String)
annotation class TestCasee(val id: Int)
annotation class TestCaseee(val id: Array)
annotation class TestCaseeee(val id: Run)
annotation class TestCaseeeeee(val id: KClass)

而下面的兩種聲明編譯不通過

annotation class TestCaseeeee(val id: Array) 
annotation class TestCaseeeeee(val id: SwordTest)

另外,需要注意的是:注解類型不能有 null 類型,因?yàn)镴VM不支持將null作為注解屬性的值存儲(chǔ)。如果注解用作另一個(gè)注解的參數(shù),其名稱不以@字符為前綴

annotation class AnnoX(val value: String)

annotation class AnnoY(
        val message: String,
        val annoX: AnnoX = AnnoX("X"))

Java注解與Kotlin完全兼容。下面是一個(gè)Kotlin使用JUnit 4進(jìn)行單元測(cè)試代碼編寫的例子

package com.easy.kotlin

import com.easy.kotlin.annotation.SwordTest
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4

@RunWith(JUnit4::class)
class AnnotationClassNoteTest {
    @Test
    fun testAnno() {
        val sword = SwordTest()
        sword.testCase("10000")
    }
}

我們可以看出,除了@RunWith(JUnit4::class) 這地方的反射寫法稍微有點(diǎn)不同外,剩下的跟我們?cè)贘ava中使用 JUnit 的注解的方式基本上是一樣的。

12.2.3 處理注解

定義了注解,并在需要的時(shí)候給相關(guān)類,類屬性加上注解信息,如果沒有相應(yīng)的注解信息處理邏輯流程,那么注解可以說是廢掉了,沒啥實(shí)用價(jià)值。如何讓注解在程序運(yùn)行的時(shí)候發(fā)揮其特有的作用呢?核心就在于注解處理的代碼了。本小節(jié)我們將學(xué)習(xí)到怎樣進(jìn)行注解信息的獲取和處理。因?yàn)樽⒔庑畔⒌墨@取主要是使用反射API,所以我們也會(huì)在本節(jié)中講到反射相關(guān)的內(nèi)容。

首先,我們的目標(biāo)測(cè)試類是

@Run
class SwordTest {

    @TestCase(id = "1")
    fun testCase(testId: String) {
        println("Run SwordTest ID = ${testId}")
    }

}

這里我們主要介紹 @TestCase 注解作用在函數(shù)上的處理過程。

::class 引用

首先,我們聲明一個(gè)變量指向 SwordTest 對(duì)象實(shí)例

 val sword = SwordTest()

然后,我們就可以通過這個(gè)變量來獲取此對(duì)象的類的信息。使用 ::class 來獲取sword對(duì)象實(shí)例的 KClass 類的引用

val kClass = sword::class

上面的這行代碼,Kotlin編譯器會(huì)自動(dòng)推斷出kClass變量的類型是

val kClass:KClass = sword::class

這個(gè)KClass 數(shù)據(jù)類型我們將在下面的小節(jié)中介紹。

declaredFunctions 擴(kuò)展屬性

下面,我們需要獲取sword對(duì)象類型所聲明的所有函數(shù)。Kotlin中可以直接使用擴(kuò)展屬性 declaredFunctions 來獲取這個(gè)類中聲明的所有函數(shù)(對(duì)應(yīng)的反射數(shù)據(jù)類型是 KFunction )。代碼如下

val declaredFunctions = kClass.declaredFunctions

返回的是一個(gè) Collection>> , 其中<> 是Kotlin泛型中的星投影,類似Java中的 通配符。

這個(gè) declaredFunctions 擴(kuò)展屬性的實(shí)現(xiàn)源碼如下

@SinceKotlin("1.1")
val KClass<*>.declaredFunctions: Collection>
    get() = (this as KClassImpl).data().declaredMembers.filterIsInstance>()
annotations 屬性

KFunction 類型繼承了 KCallable , KCallable又繼承了 KAnnotatedElement ,KAnnotatedElement 中有個(gè) public val annotations: List 屬性里面存儲(chǔ)了該函數(shù)所有的注解的信息。通過遍歷這個(gè)存儲(chǔ)Annotation 的List,我們獲取到 TestCase 注解

for (f in declaredFunctions) {
        // 處理 TestCase 注解,使用其中的元數(shù)據(jù)
        f.annotations.forEach {
            if (it is TestCase) {
                val id = it.id // TestCase 注解的屬性 id
                doSomething(id) // 注解處理邏輯
            }
        }
}
call 函數(shù)

另外,如果我們想通過反射來調(diào)用函數(shù),可以直接使用 call 函數(shù)

f.call(sword, id) 

上面的代碼等價(jià)于 f.javaMethod?.invoke(sword, id) 。

到這里,我們就完成了一個(gè)簡(jiǎn)單的注解處理器。完整的代碼如下

fun testAnnoProcessing() {
    val sword = SwordTest()
    // val kClasss:KClass = sword::class // 類型聲明可省略
    val kClass = sword::class

    val declaredFunctions = kClass.declaredFunctions // 獲取sword對(duì)象類型所聲明的所有函數(shù)
    println(declaredFunctions)

    for (f in declaredFunctions) {
        // 處理 TestCase 注解,使用其中的元數(shù)據(jù)
        f.annotations.forEach {
            if (it is TestCase) {
                val id = it.id
                doSomething(id) // 注解處理邏輯
                f.call(sword, id) // 等價(jià)于 f.javaMethod?.invoke(sword, id)
            }
        }
    }
}

private fun doSomething(id: String) {
    println("Do Something in Annotation Processing ${id} ${Date()} ")
}

@Target(AnnotationTarget.CLASS,
        AnnotationTarget.FUNCTION,
        AnnotationTarget.VALUE_PARAMETER,
        AnnotationTarget.EXPRESSION)
@Retention(AnnotationRetention.RUNTIME)
@Repeatable
@MustBeDocumented
annotation class TestCase(val id: String)

class SwordTest {

    @TestCase(id = "1")
    fun testCase(testId: String) {
        println("Run SwordTest ID = ${testId}")
    }

}

測(cè)試代碼

fun main(args: Array) {
    testAnnoProcessing()
}

輸出

[fun com.easy.kotlin.annotation.SwordTest.testCase(kotlin.String): kotlin.Unit]
Do Something in Annotation Processing 1 Mon Oct 23 23:04:09 CST 2017 
Run SwordTest ID = 1
12.3 反射

在上面小節(jié)中的注解信息的獲取與處理邏輯的實(shí)現(xiàn)中,其實(shí)我們已經(jīng)用到了反射。反射是指在運(yùn)行時(shí)(Run Time),程序可以訪問、檢測(cè)和修改它本身狀態(tài)或行為的一種能力。Kotlin中的函數(shù)和屬性也是頭等公民,我們可以通過反射來內(nèi)省屬性和函數(shù):如運(yùn)行時(shí)屬性名或類型,函數(shù)名或類型等。

在Kotlin中我們有兩種方式來實(shí)現(xiàn)反射的功能。一種是調(diào)用Java 的反射包 java.lang.reflect 下面的API ,另外一種方式就是直接調(diào)用Kotlin語言提供的kotlin.reflect 包下面的API 。 不過因?yàn)榉瓷涔δ艿膽?yīng)用場(chǎng)景并非所有編程場(chǎng)景都用到,所有Kotlin把kotlin.reflect 包的實(shí)現(xiàn)放到了多帶帶的kotlin-reflect-1.1.50.jar (當(dāng)前版本號(hào)是1.1.50) 里面。所以在實(shí)際工程中,如果我們需要使用Kotlin的反射功能,以Gradle為例,需要在build.gradle配置文件中添加依賴

compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"

Kotlin反射API類的層次結(jié)構(gòu)如下圖所示

12.3.1 類引用

為了方便講解,我們先定義一個(gè)代碼實(shí)例

open class BaseContainer

class Container> : BaseContainer {
    var elements: MutableList

    constructor(elements: MutableList) {
        this.elements = elements
    }

    fun sort(): Container {
        elements.sort()
        return this
    }

    override fun toString(): String {
        return "Container(elements=$elements)"
    }

}

反射是在運(yùn)行時(shí)獲取一個(gè)類引用。我們已經(jīng)知道使用 ::class 調(diào)用可以獲取到當(dāng)前對(duì)象的 KClass對(duì)象

val container = Container(mutableListOf(1, 3, 2, 5, 4, 7, 6))
val kClass = container::class // 獲取KClass對(duì)象

需要注意的是,Kotlin中類引用和Java中類引用是不同的,要獲得java類的引用,可以直接使用 javaClass 這個(gè)擴(kuò)展屬性

val jClass = container.javaClass // 獲取Java Class對(duì)象

javaClass 擴(kuò)展屬性在Kotlin中的實(shí)現(xiàn)源碼是

public inline val  T.javaClass : Class
    @Suppress("UsePropertyAccessSyntax")
    get() = (this as java.lang.Object).getClass() as Class

或者使用KClass實(shí)例的 .java 屬性

val jkCLass  = kClass.java

這個(gè)KClass.java 的擴(kuò)展屬性的實(shí)現(xiàn)源碼如下

@Suppress("UPPER_BOUND_VIOLATED")
public val  KClass.java: Class
    @JvmName("getJavaClass")
    get() = (this as ClassBasedDeclarationContainer).jClass as Class
12.3.2 函數(shù)引用

例如,我們有一個(gè)簡(jiǎn)單的判斷一個(gè)Int整數(shù)是否是奇數(shù)的函數(shù)

fun isOdd(x: Int) = x % 2 != 0

我們可以代碼中直接調(diào)用

>>> isOdd(7)
true
>>> isOdd(2)
false

另外,在高階函數(shù)中我們想把它當(dāng)做一個(gè)參數(shù)來使用,可以使用 :: 操作符

val nums = listOf(1, 2, 3)
val filteredNums = nums.filter(::isOdd)
println(filteredNums) // [1, 3]

這里的 ::isOdd 就是一個(gè)函數(shù)類型 (Int) ->Boolean 的值 。

12.3.3 屬性引用

在Kotlin中,訪問屬性是屬于第一級(jí)對(duì)象,我們可以使用 :: 操作符

var one = 1
fun testReflectProperty() {
    println(::one.get()) // 1
    ::one.set(2)
    println(one)         // 2
}

fun main(args: Array) {
    testReflectProperty()
}

表達(dá)式 ::one 等價(jià)于類型為KProperty的一個(gè)屬性,它可以允許我們通過 get 函數(shù)獲取值 ::one.get() 。

對(duì)于可變屬性 var one = 1 ,返回類型為KMutableProperty的值,并且還有set方法 ::one.set(2) 。

12.3.4 綁定函數(shù)和屬性引用

我們可以引用一個(gè)對(duì)象實(shí)例的方法。例如下面的代碼

val digitRegex = "d+".toRegex()
digitRegex.matches("7") // true
digitRegex.matches("6") // true
digitRegex.matches("5") // true
digitRegex.matches("X") // false

其中的 digitRegex.matches 重復(fù)出現(xiàn),顯得“樣板化”。 在Kotlin中可以直接引用digitRegex對(duì)象實(shí)例的matches方法。上面的代碼我們可以寫成下面這樣

val isDigit = digitRegex::matches  // 引用 digitRegex 對(duì)象實(shí)例的 matches 方法
isDigit("7")// true
isDigit("6")// true
isDigit("5")// true
isDigit("X")// true

是不是很酷? 真的是相當(dāng)簡(jiǎn)潔。

12.4 使用反射獲取泛型信息

在Java中,使用反射的一個(gè)代碼實(shí)例如下

package com.easy.kotlin;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

interface StudentService {
    List findStudents(String name, Integer age);
}

public class ReflectionDemo {

    public static void main(String[] args) {
        StudentServiceImpl studentService = new StudentServiceImpl();
        studentService.save(new Student("Bob", 20));
        studentService.findStudents("Jack", 20);

        // 反射API調(diào)用示例
        final Class studentServiceClass = studentService.getClass();
        Class[] classes = studentServiceClass.getDeclaredClasses();
        Annotation[] annotations = studentServiceClass.getAnnotations();
        ClassLoader classLoader = studentServiceClass.getClassLoader(); // Returns the class loader for the class
        Field[] fields = studentServiceClass.getDeclaredFields(); // 獲取類成員變量
        Method[] methods = studentServiceClass.getDeclaredMethods(); // 獲取類成員方法
        try {
            methods[0].getName(); // save
            methods[0].invoke(studentService, "Jack",20);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

}

class StudentServiceImpl extends BaseService implements StudentService {

    public List findStudents(String name, Integer age) {
        return Arrays.asList(new Student[] {new Student("Jack", 20), new Student("Rose", 20)});
    }

    @Override
    public int save(Student student) {
        return 0;
    }
}

abstract class BaseService {
    abstract int save(T t);
}

class Student {

    String name;
    Integer age;

    public Student(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

通過反射,我們可以獲取一個(gè)類的注解、方法、成員變量、方法等等。那么我們能不能通過反射獲取到泛型的信息呢?我們知道 Java中的泛型采用擦拭法。在運(yùn)行時(shí),無法得到自己本身的泛型信息。而當(dāng)這個(gè)類繼承了一個(gè)父類,父類中有泛型的信息,那么我們可以通過調(diào)用getGenericSuperclass()方法得到父類的泛型信息。getGenericSuperclass()是Generic繼承的特例,對(duì)于這種情況子類會(huì)保存父類的Generic參數(shù)類型,返回一個(gè)ParameterizedType。另外,我們所說的 Java 泛型在字節(jié)碼中會(huì)被擦除,并不總是擦除為 Object 類型,而是擦除到上限類型。

在Kotlin也是一樣的泛型機(jī)制。所以,通過反射能拿到的也只能是有繼承父類泛型信息的子類泛型。

class A

open class C
class B : C()  // 繼承父類 C()  

fun fooA() {
    // 無法在此處獲得運(yùn)行時(shí) T 的具體類型!!!運(yùn)行報(bào)錯(cuò):java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType

    val parameterizedType = A()::class.java.genericSuperclass as ParameterizedType
    val actualTypeArguments = parameterizedType.actualTypeArguments
    for (type in actualTypeArguments) {
        val typeName = type.typeName
        println("typeName = ${typeName}")
    }
}

fun fooB() {
    // 當(dāng)繼承了父類 C 的時(shí)候,在此處獲得運(yùn)行時(shí) genericSuperclass T 的具體類型
    val parameterizedType = B()::class.java.genericSuperclass as ParameterizedType
    val actualTypeArguments = parameterizedType.actualTypeArguments
    for (type in actualTypeArguments) {
        val typeName = type.typeName
        println("typeName = ${typeName}") // typeName = java.lang.Integer
    }
}

fun main(args: Array) {
    // fooA() 
    fooB() 
}

下面我們通過一個(gè)簡(jiǎn)單的實(shí)例來說明Kotlin中的反射怎樣獲取泛型代碼的基本信息。

首先,聲明一個(gè)父類 BaseContainer

open class BaseContainer

然后,聲明一個(gè) Container> 繼承它

class Container> : BaseContainer {
    var elements: MutableList

    constructor(elements: MutableList) {
        this.elements = elements
    }

    fun sort(): Container {
        elements.sort()
        return this
    }

    override fun toString(): String {
        return "Container(elements=$elements)"
    }
}

聲明一個(gè) Container對(duì)象實(shí)例

val container = Container(mutableListOf(1, 3, 2, 5, 4, 7, 6))

獲取container 的KClass 對(duì)象引用

val kClass = container::class // 獲取KClass對(duì)象

KClass對(duì)象的 typeParameters 屬性中存有類型參數(shù)的信息

val typeParameters = kClass.typeParameters // 獲取類型參數(shù)typeParameters信息,也即泛型信息

val kTypeParameter: KTypeParameter = typeParameters[0]
println(kTypeParameter.isReified) // false
println(kTypeParameter.name) // T
println(kTypeParameter.upperBounds) // [kotlin.Comparable]
println(kTypeParameter.variance) // INVARIANT

KClass的 constructors 屬性中存有構(gòu)造函數(shù)的信息,我們可以從中獲取構(gòu)造函數(shù)的入?yún)⒌刃畔?/p>

val constructors = kClass.constructors
for (KFunction in constructors) {
    KFunction.parameters.forEach {
        val name = it.name
        val type = it.type
        println("name = ${name}") // elements
        println("type = ${type}") // kotlin.collections.MutableList
        for (KTypeProjection in type.arguments) {
            println(KTypeProjection.type) // T
        }
    }
}
本章小結(jié)

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/68014.html

相關(guān)文章

  • Kotlin + Spring Boot : 下一代 Java 服務(wù)端開發(fā)

    摘要:下一代服務(wù)端開發(fā)下一代服務(wù)端開發(fā)第部門快速開始第章快速開始環(huán)境準(zhǔn)備,,快速上手實(shí)現(xiàn)一個(gè)第章企業(yè)級(jí)服務(wù)開發(fā)從到語言的缺點(diǎn)發(fā)展歷程的缺點(diǎn)為什么是產(chǎn)生的背景解決了哪些問題為什么是的發(fā)展歷程容器的配置地獄是什么從到下一代企業(yè)級(jí)服務(wù)開發(fā)在移動(dòng)開發(fā)領(lǐng)域 《 Kotlin + Spring Boot : 下一代 Java 服務(wù)端開發(fā) 》 Kotlin + Spring Boot : 下一代 Java...

    springDevBird 評(píng)論0 收藏0
  • Java學(xué)習(xí)路線總結(jié),搬磚工逆襲Java架構(gòu)師(全網(wǎng)最強(qiáng))

    摘要:哪吒社區(qū)技能樹打卡打卡貼函數(shù)式接口簡(jiǎn)介領(lǐng)域優(yōu)質(zhì)創(chuàng)作者哪吒公眾號(hào)作者架構(gòu)師奮斗者掃描主頁(yè)左側(cè)二維碼,加入群聊,一起學(xué)習(xí)一起進(jìn)步歡迎點(diǎn)贊收藏留言前情提要無意間聽到領(lǐng)導(dǎo)們的談話,現(xiàn)在公司的現(xiàn)狀是碼農(nóng)太多,但能獨(dú)立帶隊(duì)的人太少,簡(jiǎn)而言之,不缺干 ? 哪吒社區(qū)Java技能樹打卡?【打卡貼 day2...

    Scorpion 評(píng)論0 收藏0
  • Java開發(fā)

    摘要:大多數(shù)待遇豐厚的開發(fā)職位都要求開發(fā)者精通多線程技術(shù)并且有豐富的程序開發(fā)調(diào)試優(yōu)化經(jīng)驗(yàn),所以線程相關(guān)的問題在面試中經(jīng)常會(huì)被提到。將對(duì)象編碼為字節(jié)流稱之為序列化,反之將字節(jié)流重建成對(duì)象稱之為反序列化。 JVM 內(nèi)存溢出實(shí)例 - 實(shí)戰(zhàn) JVM(二) 介紹 JVM 內(nèi)存溢出產(chǎn)生情況分析 Java - 注解詳解 詳細(xì)介紹 Java 注解的使用,有利于學(xué)習(xí)編譯時(shí)注解 Java 程序員快速上手 Kot...

    LuDongWei 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<