簡體   English   中英

在Scala中調用私有Java方法

[英]Calling private Java methods in 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)
}

可以像這樣使用:

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繼承(或實現)。 如果您嘗試通過callPrivate(someObject, "someMethod", new B())這樣的方式調用Scala方法,則該方法大部分不會起作用,因為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.getClass() ,這意味着它不會返回任何繼承的方法。 我不知道這是否是設計使然,否則您將需要在obj的超類上添加遞歸查找。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM