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