有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

Kotlin在java上调用扩展函数时的奇怪行为。朗,反思一下。代理对象

今天我在科特林和一些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调用代理


共 (2) 个答案

  1. # 1 楼答案

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

    您的ProxyDog正在充当底层DogImpl实例的装饰器。当主方法调用proxyDog.bark5Times()时,扩展方法在代理对象上连续调用bark()五次,该代理对象包含建议,因此打印“代理已调用!”留言

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

  2. # 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
    }