探索Scala(3)-- 單例對象

研究一下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.classMyMath$.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);
    }

}
可以得出如下結論:

  1. val字段實際上也被編譯成了方法
  2. 兩個方法都是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文件裏。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章