Skip to content

Instantly share code, notes, and snippets.

@cy6erGn0m
Created September 15, 2021 16:32
Show Gist options
  • Save cy6erGn0m/a5d2c9bd573b8a77df3bb9f670eb035a to your computer and use it in GitHub Desktop.
Save cy6erGn0m/a5d2c9bd573b8a77df3bb9f670eb035a to your computer and use it in GitHub Desktop.
Using Java LambdaMetafactory to convert Kotlin property to lambda without reflections and faster
// Please note that you don't even need kotlin-reflect.jar
class C {
val f: String
get() = "ok"
}
fun usage() {
val fn = makeFunction(C::f)
println(fn(C()))
}
inline fun <reified Owner, reified Value> makeFunction(property: KProperty1<Owner, Value>): (Owner) -> Value {
val propertyGetter = getterMethod(Owner::class, property)
val lookup = MethodHandles.lookup()!!
val targetMethod = lookup.unreflect(propertyGetter)
return makeFunctionImpl(lookup, targetMethod, Value::class.java, Owner::class.java)
}
@PublishedApi
internal fun <Owner, Value> makeFunctionImpl(
lookup: MethodHandles.Lookup,
targetMethod: MethodHandle?,
valueJavaClass: Class<Value>,
ownerJavaClass: Class<Owner>
): (Owner) -> Value {
val factory = LambdaMetafactory.metafactory(
lookup,
Function1<*, *>::invoke.name,
MethodType.methodType(Function1::class.java),
MethodType.methodType(Any::class.java, Any::class.java),
targetMethod,
MethodType.methodType(valueJavaClass, ownerJavaClass)
)
@Suppress("UNCHECKED_CAST")
return factory.target.invoke() as Function1<Owner, Value>
}
@PublishedApi
internal fun getterMethod(clazz: KClass<*>, property: KProperty1<*, *>): Method =
clazz.java.getMethod("get" + property.name[0].titlecase(Locale.ENGLISH) + property.name.drop(1))
@OptIn(ExperimentalTime::class)
fun main() {
val count = 1000000000
val fn = makeFunction(C::f)
val instance = C()
val method = getterMethod(C::class, C::f)
println("Lambda")
var total = 0L
measureTime {
repeat(count) {
total += fn(instance).length
}
}.let {
println("Lambda $it") // 37 millis
}
println("Reflections")
measureTime {
repeat(count) {
total += (method.invoke(instance) as String).length
}
}.let {
println("Reflections $it") // 3.9 seconds
}
println("Done")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment