2

我可以通过反射检索一个方法,以某种方式将它与一个目标对象结合起来,然后将它作为一个看起来像 Scala 中的函数的东西返回(即你可以使用括号调用它)?参数列表是可变的。它不必是“一流”函数(我已经更新了问题),只是一个看起来像语法的函数调用,例如 f(args)。

到目前为止,我的尝试看起来是这样的(从技术上讲,这是伪代码,只是为了避免在帖子中添加额外的定义):

类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("调用:" + 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

当然。这是我编写的一些代码,用于向函数添加接口。这不完全是你想要的,但我认为它可以通过一些改变来适应。最困难的更改是 on invoke,您需要将调用的方法更改为通过反射获得的方法。此外,您必须注意您正在处理的接收方法是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 回答