Created
September 15, 2021 16:32
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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())) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@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