面试题答案
一键面试Kotlin 反射实现动态调用扩展函数
import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.full.declaredFunctions
// 假设存在一些类及其扩展函数
class MyClass1
fun MyClass1.extensionFunction1() = "MyClass1 extensionFunction1"
class MyClass2
fun MyClass2.extensionFunction2() = "MyClass2 extensionFunction2"
fun callExtensionFunction(className: String, functionName: String) {
try {
// 通过类名获取 KClass
val kClass: KClass<*> = Class.forName(className).kotlin
// 获取扩展函数
val function: KFunction<*>? = kClass.declaredFunctions.find { it.name == functionName }
if (function != null) {
// 创建类的实例(假设类有默认构造函数)
val instance = kClass.java.newInstance()
// 调用扩展函数
val result = function.call(instance)
println(result)
} else {
println("Function $functionName not found in class $className")
}
} catch (e: ClassNotFoundException) {
println("Class $className not found")
} catch (e: IllegalAccessException) {
println("Illegal access to class or function")
} catch (e: InstantiationException) {
println("Failed to instantiate class $className")
} catch (e: Exception) {
println("Error calling function: ${e.message}")
}
}
你可以这样调用这个函数:
fun main() {
callExtensionFunction("MyClass1", "extensionFunction1")
callExtensionFunction("MyClass2", "extensionFunction2")
}
性能问题
- 反射调用开销大:反射调用涉及到运行时查找类、函数等信息,相比直接调用函数,会有显著的性能开销。每次调用都需要进行查找和绑定操作。
- 类加载开销:通过
Class.forName(className)
加载类也会带来额外开销,特别是在频繁调用时。
优化方案
- 缓存反射结果:可以缓存
KClass
和KFunction
的查找结果,避免每次调用都进行反射查找。例如,可以使用HashMap
来存储已经查找过的类和函数。 - 静态调用替代:如果可能,在编译时确定要调用的函数,避免使用反射。这需要在设计上提前规划,例如通过策略模式或工厂模式来根据不同条件选择合适的实现类和方法。
- 延迟加载:对于
Class.forName(className)
的调用,可以考虑延迟加载,只有在真正需要使用某个类时才加载,而不是在项目启动时就加载所有可能用到的类。