文章目录
- 反射
- 获取类信息
- 函数引用
- 创建对象
- 调用方法
- 访问属性
- 绑定的方法与属性引用
- 获取泛型信息
反射
- 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
对象
绑定的方法与属性引用
- 当函数或属性不在任何类中定义时,程序直接用
::函数名或者属性名
的形式来获取函数或属性的引用。这些函数或者属性没有绑定任何对象,因此调用函数或属性时第一个参数必须传入调用者 - 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()
}