Kotlin笔记 十二(二) 反射

本文详细介绍了Kotlin中的反射机制,包括类引用、反射创建实例、构造器引用、调用方法、函数引用、访问属性值、属性引用以及绑定的方法与属性引用。通过KClass对象可以进行各种反射操作,如创建实例、调用方法、获取属性等,并且讲解了如何在Kotlin反射与Java反射之间转换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

反射

Kotlin中属性和函数与对象一样都是一等公民,可以直接通过反射获取其引用;

1.1、类引用

Kotlin中的类引用用KClass表示,引用的是KClass对象,Java的类引用是java.lang.Class对象,二者不一样;

对于已知的Kotlin类,通过以下获取其类引用:

		var c=MyClass(类名)::classs

如果已有Kotlin对象,也可使用对象获取类引用:

		var c=clz::class

如果要通过KClass获取对应的Java类引用:调用KClass对象的java属性

		c.java;

1.2、反射创建实例

方式一:

  1. 获取到KClass对象后,可以调用其createInstance()方法创建该类的实例,该方法***总是调用该列无参构造器创建的实例***,因此使用该方法的前提就是必须提供无参构造器;

方式二:
通过获取到的KClass对象的,借助其constructors获取到所有构造器,再利用条件判断返回目标构造器,最后通过目标构造器对象的call()创建出对应的实例;

class Item(var name:String){
    var price=0.0

    constructor(name: String,price:Double):this(name){
        this.price=price
    }
    constructor():this("未知商品"){

    }

}

fun main(args: Array<String>) {
    val itemClz= Item::class;
    val instance = itemClz.createInstance()
    println("createInstance()调用无参构造器创建实例")
    println(instance.name)
    println(instance.price)

    println("调用有参构造器创建实例")
    itemClz.constructors.forEach {
        if(it.parameters.size==2){
            val item = it.call("酸奶", 9.0)
            println(item.name)
            println(item.price)

        }
    }
}

1.3、构造器引用

构造器本质也是函数,即一个返回值为当前类实例的函数,可以将构造器引用当成函数使用;Kotlin允许通过"::“操作符并添加类名来引用***主构造器***(”::类名")

比如:上面的两个参数构造器,其函数类型为(String,Double)->Item

class Fruit(var name:String="未知"){
    fun test(apple:(String)->Fruit){
        val apple1: Fruit= apple("苹果")
        println("Fruit实例name属性:${apple1.name}")
    }


}

fun main(args: Array<String>) {
    val fruit = Fruit()
    fruit.test(::Fruit)
}

和函数作为方法形参也一样,当把函数赋值给变量时,通过"::函数名"既可,构造器大同小异,是通过"::类名"将构造器赋值给变量;

需要说明的是:如果要调用Kotlin构造器引用对应的Java构造器对象,则可通过KFunction的扩展属性javaConstructor来实现;
如:

::Fruit.javaConstructor

调用构造器引用的javaCostructor属性,需要导入Kotlin.reflect.jvm包,这些扩展属性属于与Java反射互相操作的部分;

1.4、调用方法

所有构造器和方法都是KFunction的实例,调用他们可以通过其call()方法;

调用方法步骤:

  1. 获取方法对象(KFunction的实例)
  2. 调用方法对象的call()

方法转函数:

方法是面向对象的,必须有主谓宾如(“猪八戒.吃(西瓜)”),因此在转函数时其形参都是会比对应方法多一个参数即:主语(方法调用者),就变了"吃(猪八戒,西瓜)"

class   Foo{
    fun test(msg:String){
        println("执行带参方法,mag=$msg")

    }

}

fun main(args: Array<String>) {
    val kclz= Foo::class
    val instance = kclz.createInstance()
    kclz.declaredMemberFunctions.forEach {
        if(it.parameters.size==2){
            it.call(instance,"Hello Kotlin")
            
        }
    }
}

1.5、函数引用

我们多次说过,函数也是一等公民,将函数赋值给引用是“::函数名”,调用函数:“引用变量(实参)”或者函数后面直接添加“()”同样表示调用函数;这儿在赋值给引用是,如果该函数存在多个重载的函数,那么Kotlin通过上下文推断调用的哪个函数;如果无法推断出引用哪个函数,编译器报错;

fun isSmall(i:String)=i.length<5

fun isSmall(i:Int)=i<5

当我们将函数赋值给未显示指定变量类型的引用,就会报错;

	var f=::isSmall
	
//这儿就必须显示声明变量类型,正确写法
	var f:(String)->Boolean=::isSmall

还需要补充一点的就是:如果将成员方法或扩展方法赋值给引用,需要使用限定:***“类名::成员/扩展方法"***。方法类型也不是函数的“(形参类型)->返回值类型”类型,同样需要使用限定即***”类名.(形参类型)->返回值类型“***

总结:

  1. 函数赋值给引用变量是"::函数名",该变量类型为函数类型是(形参类型)->返回值类型,
  2. 类方法、扩展方法赋值给引用变量时是"类名::方法名",该变量类型是"类名.(形参类型)->返回值类型"

如果要获取Kotlin函数引用的java方法对象,可以通过KFunction的扩展属性javaMethod实现:

	::方法名.javaMeethod

但是需要导入kotlin.reflect.jvm 包

总结:函数与方法引用

  1. 包级函数引用:直接通过"::函数名"赋值给引用变量 函数类型:(参数类型)->返回值类型
  2. 类成员方法与扩展方法:通过“类名::方法名”赋值给引用变量, 函数类型为:“类名.(参数类型)->返回值类型”

1.6 访问属性值

获取到KClass对象后,通过该对象来获取该类包含的属性;

  1. KProperty:代表***通用的属性***,他是KCallable的子接口
  2. KMutableProperty:代表通用的读写属性,他是KProperty的子接口
  3. KProperty0:代表无需调用者的属性(静态属性),他是KPropety的子接口
  4. KMutableProperty0:代表无需调用者的读写属性(静态读写属性),他是Kproperty的子接口
  5. Kproperty1:代表需要1个调用者的属性(成员属性),他是KProperty的子接口
  6. KMutableProperty1:代表一个调用者的读写属性(成员读写属性)他是KProperty1的子接口;
  7. Kproperty2:代表需要两个调用者的属性(扩展属性),他是KProperty的子接口
  8. KMutableProperty2:代表需要2个调用者的读写属性(扩展读写属性),他是KProperty2的子接口

从命名字面意思就可以知道:Mutable:表示读写属性,KPropertyN中的数字表示:0:表示无需调用者即静态属性,1:成员属性,2:表示扩展属性

当获取到KProperty对象后,可以调用get()方法获取该属性的值,如果是读写属性,需要设置值,则需要获取代表读写属性KMutableProperty对象,调用set()

1.7、属性引用

Kotlin同样提供了“::”符号加属性名的形式获取属性引用;获取的属性引用属于前面的KProperty及其子类接口实例;
获取到的读写属性引用,可以调用set()、get()修改、获取属性值,只读属性可以通过get()方法获取属性值

包级属性:“::属性名”

类成员属性:“类名::属性名”

在kotlin.reflect.jvm包下提供了Kotlin属性与java反射互操作的扩展属性:

kotlin属性对应java中的字段、getter、setter方法三种成员;

Kproperty类包含三个扩展属性:

  1. javaField:获取该属性的幕后字段(如果该属性有幕后字段),该属性返回java.lang.reflect.Field对象
  2. javaGetter:获取该属性的getter方法,该属性返回java.lang.reflect.Method对象
  3. javaSetter:获取该属性的setter方法(如果该属性是读写属性),该属性返回java.lang.reflect.Method对象

1.8、绑定的方法与属性引用

前面我们在获取***类中***静态属性、实例属性、静态方法、实例方法引用时,都是直接通过类名限定(类名::方法名、属性名),在获取到方法引用、属性引用时,无论是调用方法或者设置属性值时第一个参数***必须传入该方法、属性的调用者即该方法、属性所属对象实例***(参数个数=等于形参个数+方法调用者);Kotlin1.1支持绑定的方法与属性引用,方法、属性引用不是通过类(名)获取(如:类名::方法名),而是通过对象获取(如:“对象名::方法/属性名”),这样在调用该方法是将不用在传入该方法的调用者了(其实就是获取实例方法、实例属性引用);

var str="kotlin"
var f:(CharSequence,Boolean)->Boolean=str::endWith


//	无须再传入方法调用者
调用:f("lin",true)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值