Kotlin学习(12)元编程,android游戏开发大全第三版pdf

阅读 61

2022-01-25

@Run

class SwordTest() {

@TestCase(id = “a”)

fun testCase(testId: String) {

println(“Run SwordTest Id = $testId”)

}

}

我们需要在 @TestCase注解作用在函数上的处理过程

  1. ::class 引用

首先声明一个变量指向SwordTest实例

然后就可以通过这个变量来获取该对象的类的信息,使用 ::class来获取 sword对象实例的 KClass类的引用

val sword = SwordTest()

val kClass = sword::class

//有点像Java中的 getClass()

//上面这行代码,Kotlin编译器会自动推断出 kClass变量的类型是

val kClass:KClass = sword::class

  1. members扩展属性

下面我们需要获取 sword对象类型所声明的所有函数。Kotlin中 可以直接使用扩展属性 declaredFunctions来获取这个类中声明的所有函数。

//返回的是一个 Collection<KCallable<>> 其中是Koltlin泛型中的星投影

val members = kClass.members

  1. annotations属性

KFunction 类型继承了 KCallable,KCallable又继承了 KAnnotatedElement。KAnnotatedElement中有个public val annotations:List属性里存储了该函数所有的注解信息。通过遍历这个存储Annotation的List,可以获取到TestCase注解:

for (f in members ) {

f.annotations.forEach {

if(it is TestCase){

val id = it.id //TestCase 注解的属性ID

doSomething(id) //注解处理逻辑

}

}

}

  1. call函数

另外,如果想通过反射来调用函数,可以直接使用 call():

f.call(sword, id)

//等价于

f.javaMethod?.invoke(sword, id)

//到这里,我们就完成了一个简单的注解处理器,完整的代码如下:

@Target(

AnnotationTarget.CLASS,

AnnotationTarget.FUNCTION,

AnnotationTarget.VALUE_PARAMETER,

AnnotationTarget.EXPRESSION

)

@Retention(AnnotationRetention.SOURCE)

@Repeatable

@MustBeDocumented

annotation class TestCase(val id: String)

class SwordTest {

@TestCase(id = “a”)

fun testCase(testId: String) {

println(“Run SwordTest Id = $testId”)

}

}

fun testAnnoProcessing() {

val sword = SwordTest()

val kClass = sword::class

val members = kClass.members

for (f in members) {

f.annotations.forEach {

if (it is TestCase) {

val id = it.id

doSomething(id)

f.call(sword, id)

}

}

}

}

private fun doSomething(id: String) {

println(“Do Something in Annotation Processing $id ${System.currentTimeMillis()}”)

}

//测试:

main(){

testAnnoProcessing()

}

3. 反射


在Kotlin中我们有两种方式来实现反射功能。

一种是调用Java的反射包下的API

另外一种是 直接调用Kotlin语言提供的 kotlin.reflect包下面的API

不过因为反射功能的应用场景并非所有编程场景都会用到,所以Kotlin把 kotlin.reflect包放到了单独的 kotlin-reflect-1.1.xx.jar下面,也就是说我们如果要使用Kotlin的反射Api,还要去添加依赖。

3.1 类引用

我们先定义一个代码实例:

open class BaseContainer

class Container<T : Comparable> : BaseContainer {

val elements: MutableList

constructor(elements: MutableList) {

this.elements = elements

}

fun sort(): Container {

elements.sort()


return this

}

override fun toString(): String {

return “Container(elements = $elements)”

}

}

反射是在陨石时获取一个类引用。我们已经知道使用 ::class 可以获取到当前对象KClass对象。

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

val kClass = container::class

//如果是要使用Java中的类引用,就要使用javaClass

val jClass = container.javaClass

//或者使用KClass实例的.java属性

val jkClass = kClass.java

3.2 函数引用

例如,有一个简单地判断一个Int整数是否是奇数的函数:

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

//代码中调用

false

另外,在高阶函数中如果想把它当做一个参数来使用,可以使用 ::操作符

val nums = listOf(1, 2, 3)

val filteredNums = nums.filter(::isOdd)

println(filteredNums) //输出 [1,3]

这里的 ::isOdd 就是一个函数类型 (Int)->Boolean 值

3.3 属性引用

在Kotlin中,访问属性属于第一级对象,可以使用 "::"操作符

var one = 1

fun testReflectProperty() {

println(::one.get())

::one.set(2)

println(one)

}

表达式 ::one 等价于类型为KProperty的一个属性,它可以允许我们通过 get()函数获取值。

对于可以边属性 var one = 1,返回类型为 KMutableProperty的值,并且还有 set()方法

3.3 绑定函数和属性引用

val digitRegex = “\d+”.toRegex()

digitRegex.matches(“a”)

digitRegex.matches(“4”)

digitRegex.matches(“1”)

digitRegex.matches(“O”)

我们定义的 digitRegex.matches重复出现,比较显得样板化。

在Kotlin中,可以直接引用 digitRegex对象实例的matches() 方法,上面代码可以这样写:

val digitRegex = “\d+”.toRegex()

val isDigit = digitRegex::matches

isDigit(“a”)

isDigit(“4”)

isDigit(“1”)

isDigit(“O”)

4. 使用反射获取泛型信息


通过反射可以获取到类的注解、方法、成员变量等,那么能不能通过反射获取到泛型信息呢?

我们知道Java中有泛型擦除,在程序运行时就无法得到泛型。

而当这个类继承一个父类,父类中有泛型信息时,那么就可以通过 调用 getGenericSuperclass()方法得到父类的泛型信息。

getGenericSuperclass()是 Generic继承的特例。对于这种情况子类会保存父类的Generic参数类型。返回一个ParameterizedType。另外,我们所的Java泛型在字节码中会被擦除,并不总是擦除为Object类型,而是擦除到上限类型。

在Kotlin中也是一样的泛型机制,所以通过反射能拿到的 也只能是父类泛型信息的子类泛型

具体的示例代码如下:

class A

open class C

class B : C() //继承父类 C()

fun fooA() {

//无法在此处获得运行时T的具体类型 , 下面代码运行时会报错

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() {

//当继承了 父类C的时候,在此处能够获得运行时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

}

}

下面通过一个简单的实例来说明Kotlin中的反射是怎样获取泛型代码的基本信息。

首先声明一个父类BaseContainer:

ntln(“typeName = ${typeName}”) //运行会报错

}

}

fun fooB() {

//当继承了 父类C的时候,在此处能够获得运行时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

}

}

下面通过一个简单的实例来说明Kotlin中的反射是怎样获取泛型代码的基本信息。

首先声明一个父类BaseContainer:

精彩评论(0)

0 0 举报