Scala 调用处理程序导致 ClassCastException

Scala Invocation Handler causes ClassCastException

我正在尝试实现代理模式,以便在必要时可以在幕后动态换出底层实例以及触发交换的扩展方法。我以前在 Java 中实现过这个,但我在 Scala 中遇到了问题。

这是我的场景:

class Client { ...library code... }

trait DynamicClient extends Client {
   def swap: Client
}

class Provider extends Provider[DynamicClient] {
  def get():DynamicClient {
    java.lang.reflect.Proxy.newProxyInstance(
      classOf[DynamicClient].getClassLoader,
      Array(classOf[DynamicClient]),
      handler)
    .asInstanceOf[DynamicClient]
  }
}

class DynamicClientHandler extends java.lang.reflect.InvocationHandler {

  var client:Client = createNewClient()
  def swap(): {
    client = createNewClient()
    client
  }
  def createNewClient():Client: { ... }


  def invoke(proxy: AnyRef, method: java.lang.reflect.Method, args: Array[AnyRef]): AnyRef = {
      method.getDeclaringClass match {
        case dyn if dyn == classOf[DynamicClient] => swap()
        case _ => method.invoke(client, args: _*)
      }
  }
}

现在的问题是:当我从代理对象上的 DynamicClient 或对象调用方法时,它们工作得很好。

val dynamicClient = injector.instanceOf[DynamicClient]
val initial = dynamicClient.client
val client = dynamicClient.swap()
val dynamicClient.toString // "Client@1234" (Object impl of toString via client instance)
assert(client != initial) //passes just fine, the underlying client is re-initialized

对属于客户端 class 的方法的任何调用在到达调用处理程序之前都会失败。

//Both of the following scenarios fail independently of the other
//ERROR:
dynamicClient.asInstanceOf[Client]
//ERROR:
dynamicClient.doSomeClientMethod()

此异常跟踪:

java.lang.ClassCastException: com.sun.proxy.$Proxy22 cannot be cast to Client

为什么我会得到这个转换异常?与 Java 方式相比,在 Scala 中是否有更好的代理调用处理方式?

好的。我试图让您的示例真正可重现,现在变成了这样:

import java.lang.reflect.{Method, Proxy}

class Client

trait DynamicClient extends Client {
  def swap: Client
}

def mkClient =
  Proxy.newProxyInstance(
    classOf[Client].getClassLoader,
    Array(classOf[DynamicClient]),
    new DynamicClientHandler
  ).asInstanceOf[DynamicClient]


class DynamicClientHandler extends java.lang.reflect.InvocationHandler {
  val client = new Client{}

  def invoke(proxy: AnyRef, method: Method, args: Array[AnyRef]): AnyRef =
    if (method.getDeclaringClass == classOf[DynamicClient])
      swap
    else method.invoke(client, args: _*)


  def swap = createNewClient

  def createNewClient = mkClient
}

mkClient.swap

只要您将 Client 的定义中的 class 更改为 trait,此示例就会生效。

为什么?因为从 the answer you linked in your comment 可以清楚地看出 trait 扩展 class 实际上是一个限制,它只在 scala 编译器中工作。因此,从 java 的角度来看,interface DynamicClient 仍然与 class Client 没有任何共同之处,正如反射错误所说。

因此您无法真正创建 classProxy,应该考虑一些解决方法。