有没有一种简单(惯用)的方法将 java.lang.reflect.Method 转换为 Scala 函数?

发布于 2024-08-30 00:10:51 字数 1024 浏览 4 评论 0原文

我可以通过反射检索方法,以某种方式将其与目标对象组合,并将其返回为看起来像 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: _*) 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* 实际上解决了我的运行时问题,因此上述方法(修复)对于我想要完成的任务来说效果很好。我想我在这里遇到了一个更普遍的可变参数问题。

Can I retrieve a Method via reflection, somehow combine it with a target object, and return it as something that looks like a function in Scala (i.e. you can call it using parenthesis)? The argument list is variable. It doesn't have to be a "first-class" function (I've updated the question), just a syntactic-looking function call, e.g. f(args).

My attempt so far looks something like this (which technically is pseudo-code, just to avoid cluttering up the post with additional definitions):

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);
        }
    }
}

With the above I was able to get past the compiler errors, but now I have a run-time exception:

Caused by: java.lang.IllegalArgumentException: argument type mismatch

The example usage is something like:

  var f = ... some expression returning method_ref ...;
  ...
  var y = f(x) // looks like a function, doesn't it?

UPDATE

Changing the args:Any* to args:AnyRef* actually fixed my run-time problem, so the above approach (with the fix) works fine for what I was trying to accomplish. I think I ran into a more general issue with varargs here.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

倾城月光淡如水﹏ 2024-09-06 00:10:52

当然。这是我编写的一些代码,用于向函数添加接口。这并不完全是你想要的,但我认为它可以通过一些改变来适应。最困难的更改是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 代替,并进行一些更改。

Sure. Here's some code I wrote that add an interface to a function. It's not exactly what you want, but I think it can be adapted with few changes. The most difficult change is on invoke, where you'll need to change the invoked method by the one obtained through reflection. Also, you'll have to take care that the received method you are processing is apply. Also, instead of f, you'd use the target object. It should probably look something like this:

  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 _ => /* ??? */
  }

Anyway, here's the code:

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)
}

And use it like this:

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

The use of "manifest" there helps with syntax. You could write it like this:

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

One could also drop the manifest and use classOf instead with a few changes.

最美的太阳 2024-09-06 00:10:52

如果您不是在寻找采用方法名称的通用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] ,它将失败,因为它不是正确的类。 (清单对此更好,但您确实要求简单......尽管我认为“简单使用”通常比“简单对功能进行编码”更好。)

If you're not looking for a generic invoke that takes the method name--but rather, you want to capture a particular method on a particular object--and you don't want to get too deeply into manifests and such, I think the following is a decent solution:

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)
  }
}

Let's see it in action:

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

The tricky part is getting the correct type for the return value. Here it's left up to you to supply it. For example,if you supply classOf[CharSequence] it will fail because it's not the right class. (Manifests are better for this, but you did ask for simple...though I think "simple to use" is generally better than "simple to code the functionality".)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文