最近在学习scala,为了写大作业。首先推荐一个学习网站–http://scala-exercises.47deg.com/index.html,感觉学习起来比较直观。因为scala既是面向对象的,又是函数式的,好多奇怪的地方。首先Object是什么就有点不懂了。
Object
Object是一个单例模式的实现。那既然是单例模式,必然避免不了Static之类的东西,最近在看Java虚拟机的一些内容,就试了试把scala的object编译成字节码的内容,发现了一些有去的内容。
对于下面的代码,执行 scalac XXX.scala
object HelloWorld {
def main(args: Array[String]) {
var a: Int = 0
}
}
编译之后会产生两个.class文件,一个叫做HelloWorld.class,另一个叫做HelloWorld$.class,那之后用javap -c指令反编译两个class,会发现其实它的实现是这样的:
对于HelloWorld:
Compiled from "XXX.scala" public final class HelloWorld { public static void main(java.lang.String[]); Code: 0: getstatic #16 // Field HelloWorld$.MODULE\$:LHelloWorld$; 3: aload_0 4: invokevirtual #18 // Method HelloWorld$.main:([Ljava/lang/String;)V 7: return }
对于HelloWorld$:
Compiled from "XXX.scala" public final class HelloWorld$ { public static final HelloWorld\$ MODULE\$; public static {}; Code: 0: new #2 // class HelloWorld$ 3: invokespecial #12 // Method "\<init\>":()V 6: return public void main(java.lang.String[]); Code: 0: iconst_0 1: istore_2 2: return }
它用来实现单例的方法是会多一个类叫做<Object name>$(<Object name>代指Object的名字,这里就是HelloWorld,下同),这个类中会有一个static final的HelloWorld$类型的变量MODULE$,然后在static{}中会构造这个变量,在scala的object里写的所有函数,成员等等都会在这个单例的类中体现出来,而原本的<Object name>只是得到<Object name>$的静态成员MODULE$,然后基于这个成员进行访问调用。
Trick
那既然Object会多构造一个<Object name>$的类,那么如果我们在声明名为<Object name>的同时,也把<Object name>$声明为一个Object,那么会发生什么呢~
也就是代码写成这样:
object HelloWorld {
def main(args: Array[String]) {
var a: Int = 0
}
}
object HelloWorld$ {
def main(args: Array[String]) {
var a: Int = 0
}
}
那编译scala会产生三个class,分别是<Object name>.class,<Object name>$.class,<Object name>$$.class。
这时候执行scala HelloWorld,那么就会报错:
java.lang.NoSuchFieldError: MODULE$
这样的错误,真是看到都不知道是哪里的问题,当然前提是会有人写这么坑的代码0.0
PS:感觉写Scala,报错基本都是报Java的错误,好坑啊。