【问题标题】:Kotlin strange behaviour when calling extension function on java.lang.reflect.Proxy object在 java.lang.reflect.Proxy 对象上调用扩展函数时 Kotlin 的奇怪行为
【发布时间】:2019-04-06 20:57:20
【问题描述】:

今天我在 Kotlin 中玩了一些 java.lang.reflect.Proxy,我对这种行为感到惊讶:

import java.lang.reflect.Proxy

interface Dog {
  fun bark()
  fun bark3Times()
}

class DogImpl : Dog {
  override fun bark() = println("Bark!")
  override fun bark3Times() = repeat(3) { bark() }
}

fun Dog.bark5Times() = repeat(5) { bark() }

fun main(args: Array<String>) {

  val classLoader = Dog::class.java.classLoader

  val realDog: Dog = DogImpl()

  val proxyDog: Dog = Proxy.newProxyInstance(
    classLoader,
    arrayOf(Dog::class.java)
  ) { _, method, _ ->

    println("Proxy invoked! Method = ${method.name}")
    method.invoke(realDog)

  } as Dog

  println("--- Dog barking 3 times ---")
  proxyDog.bark3Times()

  println()
  println("--- Dog barking 5 times ---")
  proxyDog.bark5Times()

}

输出:

--- Dog barking 3 times ---
Proxy invoked! Method = bark3Times
Bark!
Bark!
Bark!

--- Dog barking 5 times ---
Proxy invoked! Method = bark
Bark!
Proxy invoked! Method = bark
Bark!
Proxy invoked! Method = bark
Bark!
Proxy invoked! Method = bark
Bark!
Proxy invoked! Method = bark
Bark!

问题:

为什么在第一个示例中只为bark3Times 调用而不为单独的bark 调用调用代理,但在第二个示例中它不为bark5Times 调用,但这次为每个bark 调用调用?

【问题讨论】:

  • 提示与问题无关,而不是(0 until 5).forEach{ ... }您可以使用内置函数repeatrepeat(5){ ... }
  • @Pawel 谢谢,我会将建议应用于问题以获取更多惯用代码。

标签: java kotlin reflection dynamic-proxy extension-function


【解决方案1】:

这就是所谓的自调用,并且是基于代理的 AOP(例如 Spring 的 @Transactional@Cacheable)中的主要错误来源。

您的 Proxy Dog 充当底层 DogImpl 实例的装饰器。当您的主方法调用proxyDog.bark5Times() 时,扩展方法在代理对象上连续调用bark() 五次,其中包含建议并因此打印您的“代理调用!”消息。

但是,当您调用bark3Times() 时,该调用会命中代理(打印日志消息!)...然后DogImpl 实例直接调用this.bark() 三次,不通过代理。

【讨论】:

    【解决方案2】:

    您的困惑来自对扩展功能的误解。它们被编译成 JVM 中的静态方法,并且不会神奇地“注入”到类中。当您意识到它们看起来像 Java 代码时,问题就变得很明显:

    // Kotlin
    fun Dog.bark5Times() = (0 until 5).forEach { bark() }
    
    // equivalent in Java
    public static void dogBark5Times(Dog dog){ 
        for(i = 0; i < 5; i++){ dog.bark(); }  // in this case dog is proxy so log is shown
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-27
      • 1970-01-01
      相关资源
      最近更新 更多