effectiveJava
enum

Effective Java Item3 - 用Enum實作Singleton

這篇是Effective Java - Enforce the singleton property with a private constructor or an enum type章節的讀書筆記 本篇的程式碼來自於原書內容 教學內容來自Geeksforgeeks

在看這篇文章之前 強烈建議先看過Singleton

Item3: 用Enum實作Singleton

Singleton中說明了很多實現單例的方法 大多數都是利用private constructor來實現

但之前說了那麼多實作方法 是要一步一步讓你知道單例怎麼實現 和實現上會遇到的困難

比較常見的第一個方法 eager-loading

public class Elvis {
  public static final Elvis INSTANCE = new Elvis();

  private Elvis() {
  }

  public void leaveTheBuilding() {
    System.out.println("Whoa baby, I'm outta here!");
  }
}
public static void main(String[] args) {
  Elvis elvis = Elvis.INSTANCE;
  elvis.leaveTheBuilding();
}

比較常見的第二個方法 靜態工廠

public class Elvis {
  private static final Elvis INSTANCE = new Elvis();

  private Elvis() {
  }

  public static Elvis getInstance() {
    return INSTANCE;
  }

  public void leaveTheBuilding() {
    System.out.println("Whoa baby, I'm outta here!");
  }
}
public static void main(String[] args) {
  Elvis elvis = Elvis.INSTANCE;
  elvis.leaveTheBuilding();
}

事實上 實現單例有一個最簡單的方法

public enum Elvis {
  INSTANCE;

  public void leaveTheBuilding() {
    System.out.println("Whoa baby, I'm outta here!");
  }
}
public static void main(String[] args) {
  Elvis elvis = Elvis.INSTANCE;
  elvis.leaveTheBuilding();
}

就是這麼簡單

什麼是Enum

Enumerations 枚舉 當你用在一些compile time就會知道的常數 比如說一個禮拜有幾天

enum Week
{
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
}

或是樸克牌花色

enum Suit
{
    CLUB, SPADE, HEART, DIAMOND;
}

用法呢 簡單

Suit s1 = Suit.CLUB;
System.out.println(s1); // print "CLUB"

在java裡面的enum比在C++的強 enum裡面還可以加instance variable或是函數甚至是constructor 事實上用起來很像一個class

你可以把上面的enum Suit想成這樣

Class Suit
{
    public static final Suit CLUB = new Suit();
    public static final Suit SPADE = new Suit();
    public static final Suit HEART = new Suit();
    public static final Suit DIAMOND = new Suit();
}

每一個enum constant都是一個enum type的物件

既然每個enum constant都是public static 我們可以直接從enum name取得而不需要創造物件

Suit s1 = Suit.CLUB;
System.out.println(s1); // print "CLUB"

既然每個enum constant都是final 我們不能繼承enum 既然不能被繼承 那所有的method都必須是concrete method

注意enum的constructor不能是public或protected 所以枚舉對象無法再run-time的時候由call constructor來初始化

那enum的constructor什麼時候會被call呢 在load enum這個class的時候 每個enum constant會跑一次constructor

enum Suit
{
    CLUB, SPADE, HEART, DIAMOND;
 
    private Suit()
    {
        System.out.println("Constructor called for : " +
        this.toString());
    }
    public void suitInfo()
    {
        System.out.println("Universal Suit");
    }
}
 
public class Test
{    
    public static void main(String[] args)
    {
        Suit s1 = Suit.CLUB;
        System.out.println(s1);
        s1.suitInfo();
    }
}
Constructor called for : CLUB
Constructor called for : SPADE
Constructor called for : HEART
Constructor called for : DIAMOND
CLUB
Universal Suit

Alt text

Enum是實現Singleton最好的方法

剛剛講了那麼多的特性 有沒有感覺到Enum perfectly fits our requirement?

public enum Elvis {
  INSTANCE;

  public void leaveTheBuilding() {
    System.out.println("Whoa baby, I'm outta here!");
  }
}

INSTANCE是個public static final 而且在class loading的時候創建

簡直簡單完美 而且enum還支援序列化 簡直皆大歡喜 下次當你的程式需要實現Singleton 考慮一下enum吧!