通过Kotlin编译为JVM字节码的方式的实验,我构建了以下简单脚本:
package testFunctions
import java.io.StringWriter
import java.io.PrintWriter
class ToHex : (Int) -> String {
override fun invoke(p: Int): String {
val e = Exception()
val stackTrace = with(StringWriter()) {
with(PrintWriter(this)) {
e.printStackTrace(this)
flush()
}
toString()
}
println(stackTrace)
return p.toString(16).toUpperCase()
}
}
typealias IntToStringFunction = (Int) -> String
fun main(args: Array<String>) {
val toHex = ToHex()
println("154 to hex: ${toHex(154)}")
val toHexLambda: (Int) -> String = { p ->
val e = Exception()
val stackTrace = with(StringWriter()) {
with(PrintWriter(this)) {
e.printStackTrace(this)
flush()
}
toString()
}
println(stackTrace)
p.toString(16).toUpperCase()
}
println("155 to hex: ${toHexLambda(155)}")
val toHexAlias = toHexLambda as IntToStringFunction
println("156 to hex: ${toHexAlias(156)}")
}
看看生成的字节码,我看到testFunctions.ToHex
和testFunctions.TestFunctionsKt$main$toHexLambda$1
类都被编译为实现kotlin.jvm.functions.Function1
并定义了一个公共的
final String invoke(int p)
方法。由于此方法显然不会覆盖接口中定义的抽象方法,因此编译器会添加带有签名的合成桥
/* syntethic bridge */ Object invoke(Object p)
现在,查看主要功能中这些调用的链接方式,我注意到以下行为:
从打印在程序输出中的堆栈跟踪中可以明显看出这种现象:
java.lang.Exception
at testFunctions.ToHex.invoke(testFunctions.kt:8)
at testFunctions.TestFunctionsKt.main(testFunctions.kt:25)
154 to hex: 9A
java.lang.Exception
at testFunctions.TestFunctionsKt$main$toHexLambda$1.invoke(testFunctions.kt:28)
at testFunctions.TestFunctionsKt$main$toHexLambda$1.invoke(testFunctions.kt)
at testFunctions.TestFunctionsKt.main(testFunctions.kt:39)
155 to hex: 9B
java.lang.Exception
at testFunctions.TestFunctionsKt$main$toHexLambda$1.invoke(testFunctions.kt:28)
at testFunctions.TestFunctionsKt$main$toHexLambda$1.invoke(testFunctions.kt)
at testFunctions.TestFunctionsKt.main(testFunctions.kt:42)
156 to hex: 9C
如您所见,testFunctions.kt:39
和testFunctions.kt:42
都调用委托给实际方法实现的合成桥方法。
所以,现在我的问题是:考虑到在ToHex
函数化的用法中,编译器可以使用使用原始类型的方法来优化调用,而函数类型(Int) -> String
不能接受null
作为输入,为什么Kotlin编译器无法意识到可以避免对合成桥方法的调用?