2

リフレクションを介してメソッドを取得し、何らかの方法でそれをターゲット オブジェクトと組み合わせて、Scala の関数のように見えるものとして返すことはできますか (つまり、括弧を使用して呼び出すことができます)? 引数リストは可変です。「ファーストクラス」の関数である必要はありません (質問を更新しました)。f(args) など、構文的に見える関数呼び出しだけです。

これまでの私の試みは次のようになります (これは技術的には疑似コードであり、追加の定義で投稿が混乱するのを避けるためです):

class method_ref(o: AnyRef, m: java.lang.reflect.Method) {
    def apply(args: Any*): some_return_type = {
        var oa: Array[Object] = args.toArray.map { _.asInstanceOf[Object] }
        println("calling: " + m.toString + " with: " + oa.length)

        m.invoke(o, oa: _*) 一致 {
            ケース x: some_return_type => x;
            case u => throw new Exception("unknown result" + u);
        }
    }
}

上記により、コンパイラ エラーを回避できましたが、実行時例外が発生しました。

原因: java.lang.IllegalArgumentException: 引数の型が一致しません

使用例は次のようなものです。

  var f = ... method_ref を返す何らかの式 ...;
  ...
  var y = f(x) // 関数のようですね。

アップデート

args:Any* を args:AnyRef* に変更すると、実行時の問題が実際に修正されたので、上記のアプローチ (修正を含む) は、私が達成しようとしていたことに対してうまく機能します。ここで可変引数に関するより一般的な問題に遭遇したと思います。

4

2 に答える 2

4

もちろん。関数にインターフェイスを追加するために私が書いたコードを次に示します。それはまさにあなたが望むものではありませんが、少し変更するだけで適応できると思います。最も難しい変更は oninvokeです。ここでは、呼び出されたメソッドをリフレクションによって取得したメソッドに変更する必要があります。また、処理している受信メソッドが であることにも注意する必要がありますapply。また、 の代わりにf、ターゲット オブジェクトを使用します。おそらく次のようになります。

  def invoke(proxy: AnyRef, method: Method, args: Array[AnyRef]) = method match {
    case m if /* m is apply */ => target.getClass().getMethod("name", /* parameter type */).invoke(target, args: _*)
    case _ => /* ??? */
  }

とにかく、ここにコードがあります:

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

class Handler[T, R](f: Function1[T, R])(implicit fm: Manifest[Function1[T, R]]) extends InvocationHandler {
  def invoke(proxy: AnyRef, method: Method, args: Array[AnyRef]) = method.invoke(f, args: _*)
  def withInterface[I](implicit m: Manifest[I]) = {
    require(m <:< manifest[Function1[T, R]] && m.erasure.isInterface)
    Proxy.newProxyInstance(m.erasure.getClassLoader(), Array(m.erasure), this).asInstanceOf[I]
  }
}

object Handler {
  def apply[T, R](f: Function1[T, R])(implicit fm: Manifest[Function1[T, R]]) = new Handler(f)
}

そして、次のように使用します。

trait CostFunction extends Function1[String, Int]
Handler { x: String => x.length } withInterface manifest[CostFunction]

そこで「マニフェスト」を使用すると、構文に役立ちます。次のように記述できます。

Handler({ x: String => x.length }).withInterface[CostFunction] // or
Handler((_: String).length).withInterface[CostFunction]

マニフェストを削除して、代わりに classOf をいくつか変更して使用することもできます。

于 2010-04-18T05:11:17.817 に答える
2

メソッド名を取るジェネリックを探しているのではinvokeなく、特定のオブジェクトの特定のメソッドをキャプチャしたい場合や、マニフェストなどに深く入り込みたくない場合は、以下はまともな解決策です:

class MethodFunc[T <: AnyRef](o: Object, m: reflect.Method, tc: Class[T]) {
  def apply(oa: Any*): T = {
    val result = m.invoke(o, oa.map(_.asInstanceOf[AnyRef]): _*)
    if (result.getClass == tc) result.asInstanceOf[T]
    else throw new IllegalArgumentException("Unexpected result " + result)
  }
}

実際に見てみましょう:

val s = "Hi there, friend"
val m = s.getClass.getMethods.find(m => {
  m.getName == "substring" && m.getParameterTypes.length == 2
}).get
val mf = new MethodFunc(s,m,classOf[String])

scala> mf(3,8)
res10: String = there

注意が必要なのは、戻り値の正しい型を取得することです。ここでは、それを提供するのはあなた次第です。たとえばclassOf[CharSequence]、適切なクラスではないため、指定すると失敗します。(これにはマニフェストの方が適していますが、あなたはシンプルを求めました...ただし、「機能を簡単にコーディングできる」よりも「使いやすい」方が一般的に優れていると思います。)

于 2010-04-18T15:17:18.057 に答える