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 クラスに属するメソッドへの呼び出しは、Invocation Handler に到達する前に失敗します。

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

Ok。私はあなたの例を本当に再現できるようにしようとしました。

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

この例は、 の定義を に変更classするとすぐに機能します。traitClient

なんで?コメントでリンクした回答から、trait拡張classは実際には制限であり、scala コンパイラでのみ機能することが明らかであるためです。したがって、Javaの観点からは、反射エラーが言うように、interface DynamicClientまだ共通点はありません。class Client

Proxyそのため、 を実際に作成することはできずclass、回避策を考える必要があります。

于 2016-12-03T21:45:04.703 に答える