反射
-
Kotlin的类引用使用KClass表示,java的类引用对象是Class对象.
-
依赖反射包
compile 'org.jetbrains.kotlin:kotlin-reflect:1.3.31'
-
反射API层次结构
获取类信息
-
准备数据
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") }
-
获取类相关信息
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) } }
函数引用
-
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) }
-
如果要获取Kotlin构造器引用对应的java构造器对象,可以通过调用KFunction的扩展属性
javaConstructor
::Foo.javaConstructor ::Foo.javaClass ::Foo.javaMethod
-
函数引用,
::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) }
-
如果需要引用类的成员方法或者扩展方法,需要进行限定
String::toCharArray 引用类型不是()->CharArray 而是String.()->CharArray
-
函数组合
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)) }
创建对象
-
准备数据
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 } }
-
创建对象
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}") } } }
调用方法
-
准备数据
class Car { fun run() { println("run...") } fun printPrice(price: Double) { println("printPrice:${price}") } }
-
调用方法使用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) } } } }
访问属性
-
设置或者访问属性
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)) } } } }
-
kotlin也提供了
::PropertyName
来获取属性引用val kProperty1: KProperty1<User, Int> = User::age
-
为了Kotlin属性与java反射互操作,KProperty包含3个扩展属性
- javaField:获取该属性的幕后字段,该属性返回
java.lang.reflect.Field
对象 - javaGetter:获取该属性的getter方法,该属性返回
java.lang.reflect.Method
对象 - javaSetter:获取该属性的setter方法,该属性返回
java.lang.reflect.Method
对象
- javaField:获取该属性的幕后字段,该属性返回
绑定的方法与属性引用
-
当函数或属性不在任何类中定义时,程序直接用
::函数名或者属性名
的形式来获取函数或属性的引用。这些函数或者属性没有绑定任何对象,因此调用函数或属性时第一个参数必须传入调用者 -
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())
获取泛型信息
-
在程序运行时,无法得到自己本身的泛型信息(编译擦除,并不总是擦除为 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() }