hangscer

scala object关键字与singleton模式

2017/05/06

java中实现singleton模式的代码样例如下:

1
2
3
4
class Elvis{
public static final Elvis INSTANCE=new Elvis();
private Elvis(){}
}

1
2
3
4
5
6
7
class Elvis{
private static final Elvis INSTANCE=new Elvis();
private Elvis(){}
public static Elvis getInstance(){
return INSTANCE;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
class Elvis{
private static Elvis INSTANCE;
static {
new Elvis();
}
private Elvis(){
INSTANCE=this;
}
public static Elvis getInstance(){
return INSTANCE;
}
}

那么scala中的object关键字与singleton模式有何关系呢?

代码:

1
2
3
4
5
6
7
8
package nathan
object MyObject{
val myNameVal="jianghangVaL"
var myNameVar="jianghangVar"
lazy val myNameLazy="jianghangLazyVal"
def doSomething()=println("doSomething")
}
//MyObject.myNameVal ====> MyObject$.MODULE$.myNameVal()

scala中并没有static关键字,所以需要object来模拟类属性和类方法.
以上代码编译得到两个文件:MyObject.classMyObject$.class.
MyObject.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import nathan.MyObject$.MODULE$
public final class MyObject {
public static void doSomething() {
MODULE$.doSomething();
}
public static String myNameLazy() {
return MODULE$.myNameLazy();
}
public static void myNameVar_$eq(String var0) {
MODULE$.myNameVar_$eq(var0);
}
public static String myNameVar() {
return MODULE$.myNameVar();
}
public static String myNameVal() {
return MODULE$.myNameVal();
}
}

static方法代理单例对象里的方法.
相应的val myNameVal变量在编译后的只有同名的String myNameVal()方法,因为不可变,所以只有获取方法,没有修改方法;
var myNameVar变量可变,所以具有修改和获取两个方法:String myNameVar()void myNameVar_=(String var0);
至于lazy变量只有一个获取方法,因为lazy变量不可能是var变量.
接下来再看MyObject$.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package nathan;
import scala.runtime.BoxedUnit;
public final class MyObject$ {
public static final MyObject$ MODULE$;
private final String myNameVal;
private String myNameVar;
private String myNameLazy;
private volatile boolean bitmap$0;
static {
new MyObject$();
}
private String myNameLazy$lzycompute() {
synchronized(this) {
if(!this.bitmap$0) {
this.myNameLazy = "jianghangLazyVal";
this.bitmap$0 = true;
}
BoxedUnit var10000 = BoxedUnit.UNIT;//这是干嘛的 >:
}
return this.myNameLazy;
}
public String myNameVal() {
return this.myNameVal;
}
public String myNameVar() {
return this.myNameVar;
}
public void myNameVar_$eq(String x$1) {
this.myNameVar = x$1;
}
public String myNameLazy() {
return this.bitmap$0?this.myNameLazy:this.myNameLazy$lzycompute();
}
public void doSomething() {
.MODULE$.println("doSomething");
}
private MyObject$() {
MODULE$ = this;
this.myNameVal = "jianghangVaL";
this.myNameVar = "jianghangVar";
}
}

可以看出,这个类并没有static方法.
valvar变量在MyObject$.java中该有的都有.我们来看看lazy变量的神奇之处:

我们知道java本身不支持懒惰求值.那么scala是如何在jvm基础上实现lazy呢?
scala额外添加了一个标量,来标记lazy变量是否已经求值,如以上代码中的volatile boolean bitmap0变量,当调用String myNameLazy()时,首先检查bitmap0,为真的话,说明已经求过值了,直接返回myNameLazy变量,如果为假的话,则调用函数String myNameLazy$lzycompute(),该函数为myNameLazy变量赋值且修改bitmap0变量为真.

scala中的class与其伴生对象

现有一个类class MyClassAndObject以及其伴生对象:object MyClassAndObject.代码:

1
2
3
4
5
6
7
8
9
10
class MyClassAndObject{
var myNameVar_fromClass="jianghangVar_fromClass"
def doSomething_fromClass()=println("doSomething_class")
}
object MyClassAndObject{
var myNameVar_fromObject="jianghangVar"
def doSomething_fromObject()=println("doSomething_object")
}
println(MyClassAndObject.getClass.getName)//nathan.MyClassAndObject$
println((new MyClassAndObject).getClass.getName)//nathan.MyClassAndObject

反编译字节码得:
MyClassAndObject$.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public final class MyClassAndObject$ {
public static final MyClassAndObject$ MODULE$;
private String myNameVar_fromObject;
static {
new MyClassAndObject$();
}
public String myNameVar_fromObject() {
return this.myNameVar_fromObject;
}
public void myNameVar_fromObject_$eq(String x$1) {
this.myNameVar_fromObject = x$1;
}
public void doSomething_fromObject() {
this.MODULE$.println("doSomething_object");
}
private MyClassAndObject$() {
MODULE$ = this;
this.myNameVar_fromObject = "jianghangVar";
}
}

可以看出,这个类并没有static方法.

MyClassAndObject.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MyClassAndObject {
private String myNameVar_fromClass = "jianghangVar_fromClass";
public static void doSomething_fromObject() {
MODULE$.doSomething_fromObject();
}
public static void myNameVar_fromObject_$eq(String var0) {
MODULE$.myNameVar_fromObject_$eq(var0);
}
public static String myNameVar_fromObject() {
return MODULE$.myNameVar_fromObject();
}
public String myNameVar_fromClass() {
return this.myNameVar_fromClass;
}
public void myNameVar_fromClass_$eq(String x$1) {
this.myNameVar_fromClass = x$1;
}
public void doSomething_fromClass() {
MODULE$.println("doSomething_class");
}
}
//虽然MyClassAndObject$中的方法被MyClassAndObject用static方法代理,但是在scala中MyClassAndObject不能调用其伴生对象的方法.

结论,伴生对象中的方法依然被编译到同名类中,并用static方法代理.

scala.runtime.BoxedUnit

代码中看区别:

1
2
3
4
5
object Main extends App{
println(().getClass.getName) //void
println((():Unit).getClass.getName) // void
println((():Any).getClass.getName) //scala.runtime.BoxedUnit
}

BoxedUnit是对Unit类的包装器类型.