静态代理(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'
对于伴生对象中的方法,一样会在伴生类中生成一份静态代理