每天学一点 Kotlin -- 对象进阶:反射

2021-12-10  本文已影响0人  冯可乐同学

----《第一季Kotlin崛起:次世代Android开发 》学习笔记

总目录:每天学一点 Kotlin ---- 目录
上一篇:每天学一点 Kotlin -- 对象进阶:操作符重载

1. 反射

1.1 反射的作用和意思:同Java中一样,是一组语言和功能,它允许在运行时自省程序结构。这样可以在运行时对类获取它的一个名词或者一个属性,而对于函数能够获取它的类型。

1.2 反射的获取方式:双冒号。 举个栗子:
第一种写法:val c = Person::class
第二种写法:val c: KClass<Person> = Person::class

2. 使用反射

2.1 通过类名获取类的引用,举个栗子:

import kotlin.reflect.KClass

class ReflexPeople(name: String, age: Int) {
    val name: String = name
    val age: Int = age
}

fun testReflex1() {
    val c = ReflexPeople::class
    println(c)

    val p: KClass<ReflexPeople> = ReflexPeople::class
    println("p.simpleName = ${p.simpleName}")  // 打印类的名称
    println("p.isAbstract = ${p.isAbstract}")  // 打印当前类型是否为抽象类型

    val p1 = ReflexPeople("xiaoMing", 20)
    println("p.isInstance(p1) = ${p.isInstance(p1)}")   // 判断参数是否是当前类型的实例
}

fun main() {
    testReflex1()
}

打印结果:

class chapter08.ReflexPeople
p.simpleName = ReflexPeople
p.isAbstract = false
p.isInstance(p1) = true

2.2 通过类的实例对象获取类的引用:

fun testReflex2() {
    val p = ReflexPeople("name", 18)
    val c = p::class
    val name = p::name
    println(name)
    println(c)
    println(c.isInstance(p))
}

fun main() {
    testReflex2()
}

打印结果:

val chapter08.ReflexPeople.name: kotlin.String
class chapter08.ReflexPeople
true

2.3 对于函数
2.3.1 举个栗子:

fun <T> compute(compution_function: (T, T) -> Unit, a: T, b: T) {
    compution_function(a, b)
}

fun addCompution(a: Int, b: Int) {
    println("$a + $b = ${a + b}")
}

fun addCompution(a: Int, b: Int) {
    println("$a + $b = ${a + b}")
}

fun addCompution(a: Int) {
    println("$a + 1 = ${a + 1}")
}

fun testReflex3() {
    // compute(addCompution, 2, 3) // ①编译报错:因为 addCompution 只是一个函数名称,并不是一个 (Int, Int)->Unit 的函数实例。
    compute(::addCompution, 2, 3) // ②
   // compute(::addCompution, 2) // 编译器报错
}

fun main() {
    testReflex3()
}

打印结果:

2 + 3 = 5

在上面的代码中:
(1) 代码①编译会报错的,具体原因在注释中已经写明了;
(2) 函数中使用反射是通过 "::函数名称"获取当前函数实例,类型就是 “(参数类型)->返回类型”的格式
(3) 而且对于重载函数,反射也是能够自动区分出来的。

2.3.2 在类中有函数类型的属性的时候也能通过构造函数把上下文的全局函数作为参数传入:

class MyCompute<T>(compution: ((T, T) -> Unit, T, T) -> Unit) {
    val compution: ((T, T) -> Unit, T, T) -> Unit = compution
}

fun testReflex4() {
    val mc = MyCompute<Int>(::compute)
    mc.compution(::addCompution, 20, 3)
}

fun main() {
    testReflex4()
}

打印结果:

20 + 3 = 23

2.3.3 如果获得了函数的实例,该怎么调用呢?举个栗子:

fun sayHello(str: String){
    println("hello, $str")
} 

fun main(args: Array<String>) {
    // ::sayHello("Kotlin") // ① 编译器报错
    ::sayHello.call("kotlin")
}

打印结果:

hello, kotlin

上面的代码中,代码①处编译器直接报错了,因为函数能够传入参数进行执行,而函数实例是不能直接加参数的方式执行的,因为函数实例内部本身没有重载调用操作符。所以需要通过 call() 方法来实现调用

2.4 对于类里面的方法,举个栗子:

class ReflexPeople(name: String, age: Int) {
    val name: String = name
    val age: Int = age

    fun printInfo(){
        println("this is the method in ReflexPeople.class")
    }
}

fun testReflex6() {
    val p1 = ReflexPeople("Kotlin-reflect", 30)
    // 第一种方式
    p1::printInfo.call()

    // 第二种方式
    ReflexPeople::printInfo.call(p1)
}

fun main() {
    testReflex6()
}

打印结果:

this is the method in ReflexPeople.class
this is the method in ReflexPeople.class

2.5 对于变量/常量,举个栗子:

val x = 12
var y = 23
fun main(args: Array<String>) {
    println(::x.get())
    ::y.set(123)
    println(::y.get())
}

打印结果:

12
123

2.6 同理,在类中的变量/常量, 举个栗子:

class ReflexPeople1(name: String, age: Int) {
    val name: String = name
    val age: Int = age

    val name_len: Int
        get() = name.length

    var address: String = ""
        get() = "addresss"
        set(value) {
            field = value
        }
}

fun testReflex8() {
    val p = ReflexPeople1("Kotlin-reflect", 30)
    println(p::name.get())
    println(p::name_len.get())
    p::address.set("reflect_set")
    println(p::address.get())
}

fun main() {
    testReflex8()
}

打印结果:

Kotlin-reflect
14
addresss

2.7 对于构造函数,举个栗子:

class ReflexPeople2(name: String, age: Int) {
    val name: String = name
    val age: Int = age

    fun printInfo() = println("name = $name, age = $age")
}

fun testReflex9(create: (String, Int) -> ReflexPeople2, name: String, age: Int) {
    val test = create(name, age)
    test.printInfo()
}

fun main() {
    testReflex9(::ReflexPeople2, "kotlin-constructor", 20)
}

打印结果:

name = kotlin-constructor, age = 20

3. 总结

3.1 通常我们都是写完代码进行编译转换为机器语言运行,因此我们通常无法在运行的时候再回头来访问我们定义的类型、函数等。但是反射使我们的这个愿望得以实现。

3.2 虽然 Kotlin 目前这方面的功能还不是特别多,但是相信随着语言的发展,我们表达代码的方式能越来越丰富,编写的程序也会越来越动态化,项目合作模式也会越来越广泛。

相关代码:https://gitee.com/fzq.com/test-demo
上一篇下一篇

猜你喜欢

热点阅读