研究一下Scala语言的单例对象(Singleton Objects),为下一篇文章做准备。
static不是关键字
上一篇文章提到过,interface并不是Scala语言关键字,可以自由使用。同样,static在Scala里也没有特殊的含义,也是可以自由使用的,如下面代码所示:
单例对象
Java并不是完美的面向对象语言,包括很多缺陷,比如允许static字段和方法,primitive类型,等等。Scala语言在这些方面都有所改进,所以号称是比Java更OO的语言。既然去掉了static关键字,那么如何像Java语言那样,表达类字段或类方法呢?Scala给出的解决方案是:单例对象。Java有一个Math类(java.lang.Math),里头全是static字段和方法,部分代码如下所示:
public final class Math {
private Math() {} // Don't let anyone instantiate this class.
public static final double PI = 3.14159265358979323846;
public static int abs(int a) {
return (a < 0) ? -a : a;
}
}
下面我们用Scala语言重写上面的Math类:
单例对象实现方式
下面看看Scala是如何实现单例对象的。观察编译结果可以看到,MyMath被编译出两个class:MyMath.class和MyMath$.class。我自己分析了一下这两个class,下面是MyMath.class的反编译结果:
public final class MyMath {
public static double PI() {
return MyMath$.MODULE$.PI();
}
public static int abs(int a) {
return MyMath$.MODULE$.abs(a);
}
}
可以得出如下结论:
- val字段实际上也被编译成了方法
- 两个方法都是static,而且只是调用MyMath$.MODULE$的相应方法
再来看MyMath$.class的反编译结果:
public final class MyMath$ {
public static final MyMath$ MODULE$;
private final double PI;
static {
new MyMath$();
}
private MyMath$() {
MyMath$.MODULE$ = this;
this.PI = 3.14;
}
public double PI() {
return this.PI;
}
public int abs(int a) {
return return (a < 0) ? -a : a;
}
}
就是普通的
单例模式,这肯定也就是
单例对象这一名称的由来。
使用单例对象
下面这段代码演示了如何使用单例对象:
看起来和使用Java静态字段或方法没啥区别,下面是反编译之后的main方法代码:
Predef$.MODULE$.println("PI is " + MyMath$.MODULE$.PI())
final int x = -18
final int y = MyMath$.MODULE$.abs(x)
伴随类和伴随对象
上面的例子中,我们定义了名为MyMath的单例对象,实际上,这并不妨碍我们定义同名的类。如下所示:
这种情况下,单例对象叫做同名类的Companion Object,类叫做单例对象的Companion Class。如果仅定了单例对象,但没有定义同名的类,那么这种情况下单例对象被叫做Standalone Object。注意:Companion Class和Object必须定义在同一个.scala文件里。