0

我正在尝试实现一个代理模式,以便我可以在必要时动态交换底层实例以及触发交换的扩展方法。我以前在 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: _*)
      }
  }
}

现在的问题是:当我从 Proxy 对象上的 DynamicClient 或 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

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

//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 方式?

4

1 回答 1

2

好的。我试图让你的例子真正可重现,这里变成了什么:

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

只要您更改classtraitin 的定义,此示例就会起作用Client

为什么?因为从您在评论中链接的答案中可以清楚地看出,trait扩展class确实是一个限制,只能在 scala 编译器中工作。因此,从 java 的角度来看,正如反射错误所说interface DynamicClient,仍然没有什么共同之处。class Client

所以你不能真正创建Proxy一个class并且应该考虑一些解决方法。

于 2016-12-03T21:45:04.703 回答