我经常使用Scala REPL进行快速Java迭代和测试,但有时我想触发类的一些私有(private)行为,并且必须重新编译代码以使方法可见。我希望能够直接在 REPL 中调用私有(private) 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)
}
可以这样使用:
scala> callPrivate(myObj, "privateMethod", arg).asInstanceOf[ReturnedClass]
但这需要为每个 n 参数方法类型定义一个几乎重复的方法(并且需要外部强制转换,但我怀疑这是不可避免的)。有没有办法重构它,以便一个函数可以处理任意数量的参数?
注意:我使用的是 Scala 2.9.1,因此我正在寻找使用 Java Reflection 的解决方案。欢迎使用 Scala Reflection 提供答案,但不要直接解决我的问题。
最佳答案
免责声明:自从我上次使用 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:_*)
}
但是有一个缺陷。如果您在某处有一个具有如下签名的方法,则这将不起作用:
public X someMethod(A parameter);
和A
由类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)):_*)
}
这应该可以解决问题。
此外,还有一件事:请记住,您使用 getDeclaredMethod
的方式只会返回在 obj.getClass()
中实现的方法(具有任何范围) >,这意味着它不会返回任何继承的方法。我不知道这是否是设计使然,如果不是,您将需要在 obj
的父类(super class)上添加递归查找。
关于java - 在 Scala 中调用私有(private) Java 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23836016/