文章目录

  • 反射
  • 获取类信息
  • 函数引用
  • 创建对象
  • 调用方法
  • 访问属性
  • 绑定的方法与属性引用
  • 获取泛型信息


反射

  1. Kotlin的类引用使用KClass表示,java的类引用对象是Class对象.
  2. 依赖反射包
compile 'org.jetbrains.kotlin:kotlin-reflect:1.3.31'
  1. 反射API层次结构

获取类信息

  1. 准备数据
import kotlin.reflect.full.*

annotation class Monitor
annotation class City(val name: String = "Beijing")

@Monitor
@City
class Person(age: Int) {
    var name: String = "jannal"

    private constructor() : this(30)

    constructor(name: String) : this(15) {
        println("一个参数的构造函数${name}")
    }

    fun info() {
        println("无参数的info")
    }

    fun info(name: String) {
        println("有name参数的info,name:${name}")
    }
    //嵌套类
    class inner
}

fun Person.run() {
    println("扩展方法run")
}
  1. 获取类相关信息
fun main() {
    val personKClass = Person::class
    val constructors = personKClass.constructors
    println("Person的全部构造器:")
    /**
     * fun <init>(): cn.jannal.reflection.Person
     * fun <init>(kotlin.String): cn.jannal.reflection.Person
     * fun <init>(kotlin.Int): cn.jannal.reflection.Person
     */
    constructors.forEach { println(it) }

    //fun <init>(kotlin.Int): cn.jannal.reflection.Person
    println("Person的主构造器:${personKClass.primaryConstructor}")

    println("Person全部方法:")
    val functions = personKClass.functions
    /**
     * fun cn.jannal.reflection.Person.info(): kotlin.Unit
     * fun cn.jannal.reflection.Person.info(kotlin.String): kotlin.Unit
     * fun cn.jannal.reflection.Person.equals(kotlin.Any?): kotlin.Boolean
     * fun cn.jannal.reflection.Person.hashCode(): kotlin.Int
     * fun cn.jannal.reflection.Person.toString(): kotlin.String
     * */
    functions.forEach { println(it) }

    println("Person声明的所有方法(不包括继承的方法):")
    /**
     * fun cn.jannal.reflection.Person.info(): kotlin.Unit
     * fun cn.jannal.reflection.Person.info(kotlin.String): kotlin.Unit
     */
    personKClass.declaredFunctions.forEach { println(it) }

    println("Person声明的所有成员方法(不包括继承的方法):")
    /**
     * fun cn.jannal.reflection.Person.info(): kotlin.Unit
     * fun cn.jannal.reflection.Person.info(kotlin.String): kotlin.Unit
     */
    personKClass.declaredMemberFunctions.forEach { println(it) }

    println("Person声明的所有属性(不包括继承的属性):")
    //var cn.jannal.reflection.Person.name: kotlin.String
    personKClass.declaredMemberProperties.forEach { println(it) }

    println("Person的全部注解:")
    /**
     * @cn.jannal.reflection.Monitor()
     * @cn.jannal.reflection.City(name=Beijing)
     */
    personKClass.annotations.forEach { println(it) }


    println("Person的嵌套类:")
    //class cn.jannal.reflection.Person$inner
    personKClass.nestedClasses.forEach { println(it) }
}

函数引用

  1. Kotlin允许通过::ClassName来引用改类的主构造器
class Foo(var name: String = "jannal")

//test要求传入一个(String) -> Foo类型的参数
fun test(foo: (String) -> Foo) {
    val x: Foo = foo("jannal2")
    println(x.name)
}

fun main() {
    test(::Foo)
}
  1. 如果要获取Kotlin构造器引用对应的java构造器对象,可以通过调用KFunction的扩展属性javaConstructor
::Foo.javaConstructor
::Foo.javaClass
::Foo.javaMethod
  1. 函数引用,::MethodName可以获取特定函数的引用。如果有多个重载函数,kotlin可通过上下文推断,如果无法推断就会报错,此时需要手动指定
fun isSmall(i: Int) = i < 5
fun isSmall(s: String) = s.length < 5

fun main() {
    //系统可推断出来是isSmall(i: Int)
    val list = listOf(10, 20, 30, 100, -1, -20)
    list.filter(::isSmall).forEach { println(it) }
    //系统可推断出来是 isSmall(s: String)
    val list2 = listOf("10111", "2011", "30")
    list2.filter(::isSmall).forEach { println(it) }

    //系统无法推断,需要手动指定
    //val f =::isSmall
    val f: (String) -> Boolean = ::isSmall
    println(f)
}
  1. 如果需要引用类的成员方法或者扩展方法,需要进行限定
String::toCharArray
引用类型不是()->CharArray 而是String.()->CharArray
  1. 函数组合
fun abs(d: Double): Double = if (d < 0) -d else d
fun sqrt(d: Double): Double = Math.sqrt(d)

fun composition(fun1: (Double) -> Double, fun2: (Double) -> Double): (Double) -> Double {
    return { x -> fun2(fun1(x)) }
}

fun main() {
    println(abs(-3.2))
    val f = composition(::abs, ::sqrt)
    println(f(-2.0))
}

创建对象

  1. 准备数据
class Student(var name: String) {
    var age: Int = 10

    constructor() : this("jannal") {
        this.age = 20
    }

    constructor(name: String, age: Int) : this() {
        this.age = age
        this.name = name
    }
}
  1. 创建对象
fun main() {
    val studentKClass = Student::class
    val createInstance = studentKClass.createInstance();
    //name:jannal,age:20
    println("name:${createInstance.name},age:${createInstance.age}")

    //获取所有构造器
    studentKClass.constructors.forEach {
        if (it.parameters.size == 2) {
            val instance = it.call("jannal1", 50)
            //name:jannal1,age:50
            println("name:${instance.name},age:${instance.age}")

        }
    }
}

调用方法

  1. 准备数据
class Car {
    fun run() {
        println("run...")
    }

    fun printPrice(price: Double) {
        println("printPrice:${price}")
    }
}
  1. 调用方法使用call函数
fun main() {
    val carKClass = Car::class
    val createInstance = carKClass.createInstance()

    val declaredFunctions = carKClass.declaredFunctions
    declaredFunctions.forEach {

        when (it.parameters.size) {
            // 函数具有三个参数,对应两个参数,因为还有一个this
            1 -> {
                it.call(createInstance)
            }
            // 函数具有2个参数,对应1个参数,因为还有一个this
            2 -> {
                it.call(createInstance, 13.00)
            }
        }
    }
}

访问属性

  1. 设置或者访问属性
class User {
    var name: String = "username"
    val age: Int = 16
}

fun main() {
    val userKClass = User::class
    val user = userKClass.createInstance()
    //获取声明的属性
    userKClass.declaredMemberProperties.forEach {
        when (it.name) {
            "name" -> {
                //获取KProperty对象之后,通过get获取属性值
                //如果要设置值通过KMutableProperty对象
                val mp = it as KMutableProperty1<User, Any>
                mp.set(user, "jannal")
                //jannal
                println(it.get(user))
            }
            "age" ->{
                //只读属性只能通过get
                println(it.get(user))
            }
        }
    }
}
  1. kotlin也提供了::PropertyName来获取属性引用
val kProperty1: KProperty1<User, Int> = User::age
  1. 为了Kotlin属性与java反射互操作,KProperty包含3个扩展属性
  • javaField:获取该属性的幕后字段,该属性返回java.lang.reflect.Field对象
  • javaGetter:获取该属性的getter方法,该属性返回java.lang.reflect.Method对象
  • javaSetter:获取该属性的setter方法,该属性返回java.lang.reflect.Method对象

绑定的方法与属性引用

  1. 当函数或属性不在任何类中定义时,程序直接用::函数名或者属性名的形式来获取函数或属性的引用。这些函数或者属性没有绑定任何对象,因此调用函数或属性时第一个参数必须传入调用者
  2. kotlin1.1开始支持绑定的方法与属性引用.方法或属性引用不是通过类获取的而是通过对象获取的。
//kotlin1.1开始可以通过对象绑定
    val str = "jannal"
    val f: (CharSequence, Boolean) -> Boolean = str::endsWith
    //无需传入调用者
    println(f("1", true))

    val prop = str::length
    //无需传入调用者
    println(prop.get())

获取泛型信息

  1. 在程序运行时,无法得到自己本身的泛型信息(编译擦除,并不总是擦除为 Object类型,而是擦除到上限类型 ),而当这个类继承了一个父类,父类中有泛型的信息时,那么就可以通过调用getGenericSuperclass()方法得到父类 的泛型信息
class A<T>
open class C<T>
class B<T> : C<Int>()

fun test1() {
    // 无法获取运行时T的具体类型,以下运行会报错。
    // 因为java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
    val parameterizedType = A<Int>()::class.java.genericSuperclass as ParameterizedType
    parameterizedType.actualTypeArguments.forEach {
        val typeName = it.typeName
        println("typeName=${typeName}")
    }
}

fun test2() {
    val parameterizedType = B<Int>()::class.java.genericSuperclass as ParameterizedType
    parameterizedType.actualTypeArguments.forEach {
        val typeName = it.typeName
        //typeName=java.lang.Integer
        println("typeName=${typeName}")
    }
}

fun main() {
    //test1()
    test2()
}