最近在学习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的错误,好坑啊。

评论