反射调用Kotlin类里的Companion函数

支持重载、检查可空类型

Posted by Vove on November 20, 2018

支持重载、检查可空类型

此时有个类C

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class C {
    companion object {
        fun a() {
            println("a")
        }
        fun b(s: String?) {
            println("b string $s")
        }
        fun b(a: Int) {
            println("b int $a")
        }
        fun c(): String {
            return "c 12345"
        }
        fun e(a: Int, b: Int, c: String?): String {
            return "$c $a + $b = ${a + b}"
        }
    }
}

现在反射C类中的companion函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
fun kotlinReflectTest() {
    val cls = Class.forName("cn.vassistant.repluginapi.C")
    KotlinReflectHelper.invokeCompanionMethod(cls, "a")//无参数函数
    KotlinReflectHelper.invokeCompanionMethod(cls, "b", "sss")//可空参数函数
    KotlinReflectHelper.invokeCompanionMethod(cls, "b", null)//可空参数函数
    KotlinReflectHelper.invokeCompanionMethod(cls, "b", 1)//重载函数
    val s = KotlinReflectHelper.invokeCompanionMethod(cls, "c")//返回值函数
    println(s)

    var r = KotlinReflectHelper.invokeCompanionMethod(cls, "e", 1, 2, "求和")//多参数函数
    println(r)
    r = KotlinReflectHelper.invokeCompanionMethod(cls, "e", 1, 2, null)//多参数函数
    println(r)
}

输出结果:

1
2
3
4
5
6
7
a
b string sss
b string null
b int 1
c 12345
求和 1 + 2 = 3
null 1 + 2 = 3

其中KotlinReflectHelper类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
object KotlinReflectHelper {
    /**
     * 反射调用Kotlin类里的Companion函数
     * 支持函数重载调用
     * 注:参数位置严格匹配
     *
     * Companion函数的第一个参数为[Companion]
     * @param cls Class<*> javaClass
     * @param name String 函数名
     * @param args Array<out Any?> 参数
     * @return Any? 返回值
     */
    @Throws
    fun invokeCompanionMethod(cls: Class<*>, name: String, vararg args: Any?): Any? {
        val k = Reflection.createKotlinClass(cls)
        k.companionObject?.declaredFunctions?.forEach eachFun@{ f ->
            val ps = f.parameters
            if (f.name == name && ps.size == args.size + 1) {//函数名相同, 参数数量相同
                //匹配函数参数
                ps.subList(1, ps.size).withIndex().forEach {
                    val paramType = it.value.type
                    val userParam = args[it.index]
                    val canNullAndNull = paramType.isMarkedNullable && userParam == null  //isMarkedNullable参数类型是否可空
                    if (!canNullAndNull) {
                        val typeMatch = when {//判断类型匹配
                            paramType.isMarkedNullable -> //参数可空 userParam不空 转不可空类型比较
                                paramType.withNullability(false).isSubtypeOf(userParam!!::class.defaultType)

                            userParam != null -> // 参数不可空 且用户参数不空
                                paramType.isSubtypeOf(userParam::class.defaultType)//类型匹配
                            else -> return@eachFun//参数不可空 用户参数空 不符合
                        }
                        if (!typeMatch) return@eachFun //匹配下一个函数
                    }
                    //else 参数可空 给定参数空 过
                }
                val argsWithInstance = arrayOf(k.companionObjectInstance, *args)
                return f.call(*argsWithInstance)
            }
        }
        throw Exception("no companion method named : $name")
    }

}
  1. 添加依赖implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" 或下载kotlin-reflect.jar

另一种方式

指定参数类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
    /**
     * 
     * @param cls Class<*> 类 可通过Class.forName(..) 获取
     * @param name String 函数名
     * @param argPairs Array<out Pair<Class<*>, Any?>> first: 值类型, second: 值
     * @return Any? 函数返回值
     */
    @Throws
    fun invokeCompanion(cls: Class<*>, name: String, vararg argPairs: Pair<Class<*>, Any?>): Any? {
//        println("调用函数:  ----> $name(${Arrays.toString(argPairs)})")
        val types = arrayOfNulls<KClass<*>>(argPairs.size)
        val args = arrayOfNulls<Any>(argPairs.size)
        var i = 0
        argPairs.forEach {
            types[i] = Reflection.createKotlinClass(it.first)
            args[i++] = it.second
        }

        val k = Reflection.createKotlinClass(cls)
        k.companionObject?.declaredFunctions?.forEach eachFun@{ f ->
//            println("invokeCompanionMethod 匹配函数 ----> ${f.name}()")
            val ps = f.parameters
            if (f.name == name && ps.size == argPairs.size + 1) {//函数名相同, 参数数量相同
                //匹配函数参数
                ps.subList(1, ps.size).withIndex().forEach {
                    val paramType = it.value.type
                    val userType = types[it.index]!!.defaultType
                    val typeMatch = paramType.withNullability(false).isSubtypeOf(userType)
                    if (!typeMatch) {
//                        println("invokeCompanionMethod  ----> 参数类型不匹配")
                        return@eachFun //匹配下一个函数
                    }
                    //else 参数可空 给定参数空 过
                }
                val argsWithInstance = arrayOf(k.companionObjectInstance, *args)
                return f.call(*argsWithInstance)
            } else {
//                println("invokeCompanionMethod  ----> 名称or参数数量不同")
            }
        }
        throw Exception("no companion method named : $name")
    }

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
fun kotlinReflectTest2() {
    val cls = Class.forName("cn.vassistant.repluginapi.C")
    KotlinReflectHelper.invokeCompanion(cls, "a")//无参数函数
    KotlinReflectHelper.invokeCompanion(cls, "b", Pair(String::class.java, "sss"))//可空参数函数
    KotlinReflectHelper.invokeCompanion(cls, "b", Pair(Int::class.java, null))//可空参数函数
    KotlinReflectHelper.invokeCompanion(cls, "b", Pair(Int::class.java, 1))//重载函数
    val s = KotlinReflectHelper.invokeCompanion(cls, "c")//返回值函数
    println(s)

    var r = KotlinReflectHelper.invokeCompanion(cls, "e", Pair(Int::class.java, 1),
            Pair(Int::class.java, 2), Pair(String::class.java, "求和"))//多参数函数
    println(r)
    r = KotlinReflectHelper.invokeCompanion(cls, "e", Pair(Int::class.java, 1),
            Pair(Int::class.java, 2), Pair(String::class.java, null))//多参数函数
    println(r)
}