天天看點

證明 scala 不能從外部調用内部函數

一段代碼

object ASD {
    def main(args: Array[String]): Unit = {
        def f(a: Any): Unit = {
            println(a)
        }

        f("sfsfsfdsdfd")

    }
}
           

如果我們想進行類似

ASD.main.f(xx)

ASD.f(xx)

的操作, 是否可行.

事實是殘酷的, 它告訴我們不可行.

那麼下面從反編譯角度來探究為什麼不可行:

首先我們打開編譯後的 class 檔案所在目錄

有2個檔案:

ASD.class

ASD$.class

ASD.class 節選

public final class ASD {
  public static void main(String[] paramArrayOfString) {
    ASD$.MODULE$.main(paramArrayOfString);
  }
}
           

這個不重要

ASD$.class 節選

public final class ASD$ {
  public static final ASD$ MODULE$;
  
  private final void f$1(Object a) {
    Predef$.MODULE$.println(a);
  }
  
  public void main(String[] args) {
    f$1("sfsfsfdsdfd");
  }
  
  private ASD$() {
    MODULE$ = this;
  }
}
           

發現了嗎, 内部函數 f 被編譯為 私有的 final 方法f$1.

是以我們是無法從外部直接調用的.

但是理倫上我們是可以在 ASD 内部調用這個函數的.

是真的嗎,你也是這麼想的嗎.

我們試試

this.f$1(xxx)

但是很遺憾,

value f$1 is not a member of object ASD

編譯器看不懂我們意圖, 甚至IDE都直接否決了我們的異想.

那麼我們就是要調用這個方法怎麼辦呢.

難道 javac 就注定要成為遙不可及的高冷存在嗎.

我們難道就沒有更進一步的發展了嗎.

不, 偉大的 semi 醬告訴你,阻礙是不存在的.

“反射, JVM最大的外挂.”

val clazz = Class.forName("scala.ASD$")
val m = clazz.getDeclaredMethod("f$1", classOf[Object])
val o = clazz.getDeclaredField("MODULE$").get(null)
m.setAccessible(true)
m.invoke(o, "234234234".asInstanceOf[Object])
           

わぁ、ありがとう、せみちゃん。