我可以通过反射检索一个方法,以某种方式将它与一个目标对象结合起来,并将它作为一个看起来像Scala中的函数的东西返回(即你可以用括号调用它)?参数列表是可变的。它不一定是“一流”功能(我已经更新了问题),只是一个看起来像语法的函数调用,例如F(参数)。
到目前为止,我的尝试看起来像这样(技术上是伪代码,只是为了避免使用其他定义混淆帖子):
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: _*) match { case x: some_return_type => x; case u => throw new Exception("unknown result" + u); } } }
通过上面的介绍,我能够解决编译器错误,但现在我遇到了运行时异常:
Caused by: java.lang.IllegalArgumentException: argument type mismatch
示例用法类似于:
var f = ... some expression returning method_ref ...; ... var y = f(x) // looks like a function, doesn't it?
更新
更改args:Any *到args:AnyRef *实际上修复了我的运行时问题,所以上面的方法(使用修复)适用于我想要完成的任务。我想我在这里遇到了一个更普遍的问题。
答案 0 :(得分:4)
不确定。这是我编写的一些代码,用于为函数添加接口。这不完全是你想要的,但我认为它可以适应很少的变化。最困难的更改是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]
使用“manifest”有助于语法。你可以这样写:
Handler({ x: String => x.length }).withInterface[CostFunction] // or
Handler((_: String).length).withInterface[CostFunction]
还可以删除清单并使用classOf进行一些更改。
答案 1 :(得分: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]
,它将失败,因为它不是正确的类。 (清单对此更好,但你确实要求简单......虽然我认为“简单易用”通常比“简单编写功能”更好。)