【问题标题】:Scala Invocation Handler causes ClassCastExceptionScala 调用处理程序导致 ClassCastException
【发布时间】:2017-04-18 12:51:08
【问题描述】:

我正在尝试实现一个代理模式,以便我可以在必要时动态交换底层实例以及触发交换的扩展方法。我以前在 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 或 Proxy 对象上的 Object 调用方法时,它们工作得很好。

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

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

//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

为什么我会得到这个转换异常?有没有更好的方法来处理 Scala 中的代理调用处理与 Java 方式?

【问题讨论】:

  • 我认为你的代码有一些不准确之处,traits,我相信,不能extendclasses
  • 但是,这肯定是问题所在。我只是想知道为什么

标签: scala reflection proxy proxy-pattern


【解决方案1】:

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

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 没有任何共同之处,正如反射错误所说。

因此,您不能真正创建 Proxyclass,应该考虑一些解决方法。

【讨论】:

    猜你喜欢
    • 2019-06-03
    • 1970-01-01
    • 2021-04-29
    • 2017-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多