dim*_*414 2 java reflection scala
我经常使用 Scala REPL 进行快速 Java 迭代和测试,但有时我想触发类的某些私有行为,并且必须重新编译代码以使该方法可见。我希望能够直接在 REPL 中调用私有 Java 方法,而无需更改代码。
到目前为止我所得到的:
// Calls private Java methods
// We currently define an overload for every n-argument method
// there's probably a way to do this in one method?
def callPrivate(obj: AnyRef, methodName: String) = {
val method = obj.getClass().getDeclaredMethod(methodName)
val returnType = method.getReturnType
method.setAccessible(true)
println("Call .asInstanceOf[%s] to cast" format method.getReturnType.getName)
method.getReturnType.cast(method.invoke(obj))
}
def callPrivate(obj: AnyRef, methodName: String, arg: AnyRef) = {
val method = obj.getClass().getDeclaredMethod(methodName, arg.getClass())
method.setAccessible(true)
method.invoke(obj, arg)
}
Run Code Online (Sandbox Code Playgroud)
可以像这样使用:
scala> callPrivate(myObj, "privateMethod", arg).asInstanceOf[ReturnedClass]
Run Code Online (Sandbox Code Playgroud)
但这需要为每个 n 参数方法类型定义一个接近重复的方法(并且需要外部强制转换,但我怀疑这是不可避免的)。有什么方法可以重构它,以便一个函数可以处理任意数量的参数?
注意:我使用的是 Scala 2.9.1,所以我正在寻找使用 Java 反射的解决方案。欢迎使用 Scala 反射的答案,但不要直接解决我的问题。
免责声明:自从我上次用 Scala 编程以来已经有一段时间了,我周围没有任何 Scala 环境来测试我向您展示的内容。所以它可能到处都有小的语法错误,请耐心等待。希望剩下的有用
理论上,您可以为我们的callPrivate方法提供一个额外的变量参数来指定方法参数:
def callPrivate(obj: AnyRef, methodName: String, parameters:AnyRef*) = {
val parameterTypes = parameters.map(_.getClass())
val method = obj.getClass.getDeclaredMethod(methodName, parameterTypes:_*)
method.setAccessible(true)
method.invoke(obj, parameters:_*)
}
Run Code Online (Sandbox Code Playgroud)
然而有一个缺陷。如果您在某处有一个带有这样签名的方法,这将不起作用:
public X someMethod(A parameter);
Run Code Online (Sandbox Code Playgroud)
并且A由 class 继承(或实现)B。如果您尝试以这种方式调用 Scala 方法callPrivate(someObject, "someMethod", new B()),则主要是因为getDeclaredMethod查找将搜索someMethod(B)而不是someMethod(A)- 即使new B()是类型A也是如此!
所以这是一个幼稚的实现。您可能会获得所有方法参数的所有有效类型并getDeclaredMethod使用它们的所有组合执行查找,但是在这个方向上还有一个警告:您可能会遇到接受同一组参数的不同组合的重载方法你会不知道要调用的一个(即你可以有someMethod(A,B)和someMethod(B,A)你将无法知道哪一个应该被调用)
避免这种情况的一种方法是强制调用者为您提供元组而不是原始实例,每个元组都有要使用的参数值和参数类型。因此,由调用者指定他要调用的方法。
def callPrivateTyped(obj: AnyRef, methodName: String, parameters:(AnyRef,Class[_])*) = {
val parameterValues = parameters.map(_._1)
val parameterTypes = parameters.map(_._2)
val method = obj.getClass.getDeclaredMethod(methodName, parameterTypes:_*)
method.setAccessible(true)
println("Call .asInstanceOf[%s] to cast" format method.getReturnType.getName)
method.invoke(obj, parameterValues:_*)
}
// for convenience
def callPrivate(obj: AnyRef, methodName: String, parameters:AnyRef*) = {
callPrivateTyped(obj, methodName, parameters.map(c => (c, c.getClass)):_*)
}
Run Code Online (Sandbox Code Playgroud)
这应该够了吧。
另外,还有一件事:请记住,您使用的方式getDeclaredMethod只会返回在 中实现的方法(具有任何范围)obj.getClass(),这意味着它不会返回任何继承的方法。我不知道这是否是设计使然,否则您将需要在obj.
| 归档时间: |
|
| 查看次数: |
1564 次 |
| 最近记录: |