scala里的静态代理(static-forwarders)

静态代理(static-forwarders)是编译器针对从java调用scala的object里的方法提供的一种便捷。

比如,我们如何在java中调用下面object里的foo方法?

$ cat A.scala 
object A {
  def foo() = 2
}

按照通常的逻辑,我们需要在java里获取到这个单例对象,然后通过这个对象调用foo方法,不过这样做的前提你需要了解object A这个单例编译后的字节码信息:

$ javap A$
Compiled from "A.scala"
public final class A$ {
  public static final A$ MODULE$;
  public static {};
  public int foo();
}

可以看到单例中有个MODULE$的静态成员,这个MODULE$就是A$的单实例,所以我们在java中可以通过这样的形式调用: A$.MODULE$.foo()

$ cat Test.java
public class Test {
    public static void main(String[] args) {
        System.out.println(A$.MODULE$.foo());
    }
}

$ javac -cp .:/data/tools/scala/lib/scala-library.jar Test.java

$ java Test
2 

上面的方式需要你了解单例对象编译后的细节,显然不够友好。所以为了对java调用的友好,scala编译器有个静态代理机制(static-forwarders),也就是会把object中的方法以静态方式在同名的class中生成一份。

依然看最开始定义的那个object A,在编译后,其实有2个class:A.class和A$.class
(scala中为了避免static的概念,引入了object,而object本质是一个模式语法糖,而非新的概念,最终在编译时都转换为java里的class)

那么看看A.class里有什么?

$ javap A
Compiled from "A.scala"
public final class A {
  public static int foo(); //一个静态foo方法
}

// 这个静态方式内部就是去找 A$.MODULE$ 然后在调用foo的 
public static int foo();
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: getstatic     #16                 // Field A$.MODULE$:LA$;
         3: invokevirtual #18                 // Method A$.foo:()I
         6: ireturn

它的目的就是为在java中调用单例对象中的方法提供方便,我们可以直接通过 A.foo()来调用

$ cat Test.java
public class Test {
    public static void main(String[] args) {
        System.out.println(A.foo());
    }
}

$ javac -cp .:/data/tools/scala/lib/scala-library.jar Test.java

$ java Test
2 

添加静态代理的过程在编译时是可以观察到的(打开log:jvm选项):

$ scalac -Ylog:jvm A.scala
[log jvm] Dumping mirror class for 'A'
[log jvm] Adding static forwarder for 'method foo' from A to 'object A'

对于伴生对象中的方法,一样会在伴生类中生成一份静态代理

Leave a Reply

Your email address will not be published. Required fields are marked *