Kotlin把函数和属性当成“一等公民”,并可通过反射直接获取函数、属性的引用。
一、类引用
Kotlin的类引用使用KClass代表,如果想要获取已知的Kotlin类的KClass对象,可通过如下语法:
val c = MyClass : : class
如果已有一个Kotlin对象,同样可以通过 : : class语法来获取该对象的类引用。
二、从KClass获取类信息
获取KClass对象之后,可以通过KClass提供的大量方法或属性来获取该KClass对象所对应的详细信息。
package test0711
import kotlin.reflect.full.*
//定义注解
annotation class Anno
//使用3个注解修饰该类
@Deprecated("该类已经不推荐使用")
@Anno
@Suppress("UNCHECKED_CAST")
class KClassTest(age: Int) {
var name: String = "Kotlin"
//为该类定义一个私有的构造器
private constructor() : this(20) {}
//定义一个有参数的构造器
constructor(name: String) : this(15) {
println("执行有参数的构造器:${name}")
}
//定义一个无参数的info方法
fun info() {
println("执行无参数的info方法")
}
//定义一个有参数的info方法
fun info(str: String) {
println("执行有参数的info方法,其str参数值:${str}")
}
//定义一个测试用的嵌套类
class Inner
}
//为KClassTest定义扩展方法
fun KClassTest.bar() {
println("扩展的bar方法")
}
//为KClassTest定义扩展属性
val KClassTest.foo: Double
get() = 2.4
fun main(args: Array<String>) {
//获取KClassTest对应的KClass
val clazz = KClassTest::class
//通过constructors属性获取KClass对象锁对应的全部构造器
val ctors = clazz.constructors
println("KClassTes的全部构造器如下:")
ctors.forEach { println(it) }
println("ClassTest的主构造器如下:")
println(clazz.primaryConstructor)
//通过functions属性获取该KClass对象所对应类的全部方法
var funs = clazz.functions
println("KClassTest的全部方法如下:")
funs.forEach { println(it) }
//通过declaredFunctions属性获取该KClass对象声明的全部方法
var funs2 = clazz.declaredFunctions
println("KClassTest本身声明的全部方法如下:")
funs2.forEach { println(it) }
//通过declaredMemberFunctions属性获取全部成员方法
var memberFunctions = clazz.declaredMemberFunctions
println("KClassTest本身声明的成员方法如下:")
memberFunctions.forEach { println(it) }
//通过memberExtensionFunctions属性获取全部扩展方法
var exetensionFunctions = clazz.memberExtensionFunctions
println("KClassTest声明的扩展方法如下:")
exetensionFunctions.forEach { println(it) }
//通过decaredMemberProperties获取全部成员属性
var memberProperties = clazz.declaredMemberProperties
println("KClassTest本身声明的成员属性如下:")
memberProperties.forEach { println(it) }
//通过memberExtensionProperties属性获取该KClass对象的全部扩展属性
var exProperties = clazz.memberExtensionProperties
println("KClassTest本身声明的扩展属性如下:")
exProperties.forEach { println(it) }
//通过annotations属性获取该KClass对象所对应类的全部注解
val anns = clazz.annotations
println("KClassTest的全部注解如下:")
anns.forEach { println(it) }
println("该KClass元素上的@Annot注解为:${clazz.findAnnotation<Anno>()}")
//通过nestedClasses属性获取所对应的全部嵌套类
val inners = clazz.nestedClasses
println("KClassTest的全部内部类如下:")
inners.forEach { println(it) }
//通过supertypes属性获取该类的所有父类型
println("KClassTest的父类型为:${clazz.supertypes}")
}
三、创建对象
获取KClass对象之后,调用该对象的createInstance()方法即可创建该类的实例。
package test0711
import kotlin.reflect.full.createInstance
class Item(var name: String) {
var price = 0.0
constructor() : this("未知商品") {
this.price = 0.0
}
constructor(name: String, price: Double) : this(name) {
this.price = price
}
}
fun main(args: Array<String>) {
val clazz = Item::class
//createInstance()方法调用无参数的构造器创建实例
val inst1 = clazz.createInstance()
println(inst1.name)
println(inst1.price)
//获取所有构造器
val cons = clazz.constructors
cons.forEach {
if (it.parameters.size == 2) {
//调用带两个参数的构造器创建实例
val inst2 = it.call("Kotlin", 45.6)
println(inst2.name)
println(inst2.price)
}
}
}
四、构造器引用
构造器本质是一个函数,即一个返回值为当前类实例的函数。
Kotlin允许通过使用": :"操作符并添加类名来引用该类的主构造器。
package test0711
class Foo(var name: String = "未知")
//test函数的参数是(String)->Foo类型
fun test(factory: (String) -> Foo) {
val x: Foo = factory("Kotlin")
println(x.name)
}
fun main(args: Array<String>) {
//通过::Foo引用Foo类的主构造器
test(::Foo)
}
如果要获取Kotlin构造器引用对应的Java构造器对象,可通过调用KFunction的扩展属性javaConstructor来实现。
: : Foo.javaConstructor
五、调用方法
所有构造器和方法都属于KFunction的实例,可以通过call()方法来调用。
要调用指定类的方法,要先获取方法的KFunction实例,然后调用call()方法即可。
package test0711
import kotlin.reflect.full.createInstance
import kotlin.reflect.full.declaredFunctions
class CallFunction {
fun test(msg: String) {
println("执行带String参数的test方法:${msg}")
}
fun test(msg: String, price: Double) {
println("执行带String,Double参数的test方法:${msg},${price}")
}
}
fun main(args: Array<String>) {
val clazz = CallFunction::class
//创建实例
val ins = clazz.createInstance()
//获取clazz所代表类直接定义的全部函数
val funs = clazz.declaredFunctions
for (f in funs) {
//如果函数具有3个参数
if (f.parameters.size == 3) {
//调用3个参数的函数
f.call(ins, "Kotlin", 45.6)
}
//如果函数具有2个参数
if (f.parameters.size == 2) {
//调用带2个参数的函数
f.call(ins, "Kotlin")
}
}
}
六、函数引用
Kotlin的函数也有自身的类型。Kotlin可以获取函数的引用,把函数当成参数传入另一个函数中。
Kotlin也可通过“: :”符号加函数名的形式来获取特定函数的引用。
package test0711
//定义两个重载的函数
fun isSmall(i: Int) = i < 5
fun isSmall(s: String) = s.length < 5
fun main(args: Array<String>) {
val list = listOf(20, 30, 100, 4, -3, 2, -12)
val resultList = list.filter(::isSmall)
println(resultList)
val strList = listOf("Java", "Kotlin", "Swift", "Go", "Erlang")
val resultStrList = strList.filter(::isSmall)
println(resultStrList)
var f: (String) -> Boolean = ::isSmall
println(f("Lua"))
}
下面示范利用函数引用来组合两个函数
package test0711
fun abs(d: Double): Double = if (d < 0) -d else d
fun sqrt(d: Double): Double = java.lang.Math.sqrt(d)
//定义一个comp()函数,该函数用于将两个函数组合起来
fun comp(fun1: (Double) -> Double, fun2: (Double) -> Double):
(Double) -> Double {
return { x -> fun2(fun1(x)) }
}
fun main(args: Array<String>) {
println(abs(-3.2))
//将::abs和::sqrt组合起来
val f = comp(::abs, ::sqrt)
println(f(-25.0))
}
如果要获取Kotlin函数引用对应的Java方法对象,则可通过调用KFunction的扩展属性javaMethod来实现。
: : abs.javaMethod
七、访问属性值
获取KClass对象之后,也可通过KClass对象来获取该类所包含的属性。
- KProperty:代表通用的属性。
- KMutableProperty:代表通用的读写属性。
- KProperty():代表无需调用者的属性。
- KMutableProperty():代表无需调用者的读写属性。
- KProperty1:代表需要1个调用者的属性。
- KMutableProperty1:代表需要1个调用者的读写属性。
- KProperty2:代表需要2个调用者的属性。
- KMutableProperty2:代表需要2个调用者的属性。
package test0711
import kotlin.reflect.KMutableProperty1
import kotlin.reflect.full.createInstance
import kotlin.reflect.full.declaredMemberExtensionProperties
class CallItem {
var name: String = "Kotlin"
val price: Double = 7.6
}
fun main(args: Array<String>) {
val calzz = CallItem::class
val ins = calzz.createInstance()
val props = calzz.declaredMemberExtensionProperties
props.forEach {
when (it.name) {
"name" -> {
@Suppress("UNCHECKED_CAST")
//将属性转换为读写属性
val mp = it as KMutableProperty1<CallItem, Any>
//修改属性值
mp.set(ins, "Java")
println(it.get(ins))
}
}
}
}
八、属性引用
Kotlin同样提供了“: :”符号加属性名的形式来获取属性引用。
获取Kotlin只读属性的引用之后,可调用get()方法来获取属性的值;获取Kotlin读写属性的引用之后,程序可调用set()方法来修改属性值,也可调用get()方法来获取属性的值。
package test0711
class PropertyItem {
var name: String = "Kotlin"
val price: Double = 12.3
}
var ftest = "测试属性"
fun main(args: Array<String>) {
//获取foo属性
val topProp = ::ftest
topProp.set("修改后的属性")
println(topProp.get())
val im = PropertyItem()
//获取PropertyItem的name属性
val mp = PropertyItem::name
mp.set(im, "Java")
println(mp.get(im))
//获取PropertyItem的price属性
val prop = PropertyItem::price
println(prop.get(im))
}
由于Kotlin属性会对应于Java的3种成员,因此KProperty包下包含如下3个扩展属性:
- JavaField:获取该属性的幕后字段。
- javaGetter:获取该属性的getter方法。
- JavaSetter:获取该属性的setter方法。
package test0711
import kotlin.reflect.jvm.javaField
import kotlin.reflect.jvm.javaGetter
import kotlin.reflect.jvm.javaSetter
class K2JItem {
var name: String = "Kotlin"
val price: Double = 12.3
}
var k2jtest = "测试属性"
fun main(args: Array<String>) {
//获取属性
val topProp = ::k2jtest
println(topProp.javaField)
println(topProp.javaGetter)
println(topProp.javaSetter)
//获取K2JItem的name属性
val mp=K2JItem::name
println(mp.javaField)
println(mp.javaGetter)
println(mp.javaSetter)
//获取K2JItem的price属性
val prop=K2JItem::name
println(prop.javaField)
println(prop.javaGetter)
}
九、绑定的方法与属性引用
Kotlin支持一种“绑定的方法或属性引用”,这种方法或属性引用不是通过类获取的,而是通过对象获取的,这就意味着该方法或属性已经绑定了调用者,因此执行时无须传入调用者。
package test0711
fun main(args: Array<String>) {
val str = "Kotlin"
//获取对象绑定的方法
val f: (CharSequence, Boolean) -> Boolean = str::endsWith
//调用绑定的方法时无须传入调用者
println(f("lin", true))
//获取对象绑定的属性
val prop = str::length
//调用绑定的属性时无须传入调用者
println(prop.get())
var list = listOf("Kotlin", "Java", "Go", "Erlang")
//获取对象的绑定方法
val fn = list::subList
//调用绑定的方法时无须传入调用者
println(fn(1, 3))
//获取对象绑定的属性
val prp = list::indices
//调用绑定的属性时无须传入调用者
println(prp.get())
}
输出结果:
true
6
[Java, Go]
0..3
网友评论