Menerapkan Singleton dengan Enum (di Jawa)

178

Saya telah membaca bahwa adalah mungkin untuk mengimplementasikan Singletondi Jawa menggunakan Enumseperti:

public enum MySingleton {
     INSTANCE;   
}

Tetapi, bagaimana cara kerja di atas? Secara khusus, suatu Objectharus dipakai. Di sini, bagaimana MySingletonsedang dipakai? Siapa yang melakukan new MySingleton()?

Anand
sumber
24
Siapa yang melakukan MySingleton baru () The JVM adalah.
Sotirios Delimanolis
37
INSTANCEsama dengan public static final MySingleton INSTANCE = new MySingleton();.
Pshemo
6
ENUM - Singleton yang dijamin.
Bukan bug
Baca dzone.com/articles/java-singletons-using-enum , mengapa harus menggunakan enum dan bukan metode lainnya. Pendek: masalah mulai saat menggunakan serialisasi dan refleksi.
Miyagi

Jawaban:

203

Ini,

public enum MySingleton {
  INSTANCE;   
}

memiliki konstruktor kosong implisit. Jelaskan secara eksplisit,

public enum MySingleton {
    INSTANCE;
    private MySingleton() {
        System.out.println("Here");
    }
}

Jika Anda kemudian menambahkan kelas lain dengan main()metode seperti

public static void main(String[] args) {
    System.out.println(MySingleton.INSTANCE);
}

Anda akan melihat

Here
INSTANCE

enumbidang adalah kompilasi konstanta waktu, tetapi mereka adalah contoh dari enumtipe mereka . Dan, mereka dibangun ketika tipe enum direferensikan untuk pertama kalinya .

Elliott Frisch
sumber
13
Anda harus menambahkan bahwa secara default enum memiliki konstruktor pribadi implisit dan bahwa menambahkan konstruktor pribadi secara eksplisit tidak diperlukan kecuali Anda benar-benar memiliki kode yang harus dijalankan di konstruktor itu
Nimrod Dayan
setiap bidang enum membuat instance hanya sekali, jadi tidak perlu membuat konstruktor pribadi.
Pravat Panda
2
publik enum MySingleton {INSTANCE, INSTANCE1; } dan kemudian System.out.println (MySingleton.INSTANCE.hashCode ()); System.out.println (MySingleton.INSTANCE1.hashCode ()); itu mencetak kode hash yang berbeda. Apakah ini berarti dua objek MySingleton dibuat?
scott miles
@ scottmiles Ya. Karena Anda memiliki dua contoh. Seorang singleton, menurut definisi, memilikinya.
Elliott Frisch
The privatepengubah tidak memiliki arti untuk enumkonstruktor dan sepenuhnya berlebihan.
Dmitri Nesteruk
76

Suatu enumtipe adalah tipe khusus class.

Wasiat Anda enumsebenarnya akan dikompilasi menjadi sesuatu seperti

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

Ketika kode Anda pertama kali diakses INSTANCE, kelas MySingletonakan dimuat dan diinisialisasi oleh JVM. Proses ini menginisialisasi staticbidang di atas satu kali (malas).

Sotirios Delimanolis
sumber
Haruskah kelas ini juga mengandung konstruktor pribadi ()? Saya pikir itu akan terjadi
Yasir Shabbir Choudhary
public enum MySingleton { INSTANCE,INSTANCE1; }dan kemudian System.out.println(MySingleton.INSTANCE.hashCode()); System.out.println(MySingleton.INSTANCE1.hashCode());mencetak kode hash yang berbeda. Apakah ini berarti dua objek MySingleton dibuat?
scott miles
2
@ Skottmiles Ya, itu hanya dua enumkonstanta perbedaan .
Sotirios Delimanolis
2
@caf, tentu. Lihat di sini: docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3
Sotirios Delimanolis
@ SotiriosDelimanolis, apakah pendekatan ini dengan enum thread aman?
abg
63

Dalam buku praktik terbaik Java oleh Joshua Bloch, Anda dapat menemukan alasan mengapa Anda harus menegakkan properti Singleton dengan konstruktor pribadi atau tipe Enum. Bab ini cukup panjang, jadi buatlah ringkasannya:

Membuat kelas menjadi Singleton dapat membuat sulit untuk menguji kliennya, karena tidak mungkin untuk mengganti implementasi tiruan untuk singleton kecuali jika mengimplementasikan antarmuka yang berfungsi sebagai tipenya. Pendekatan yang disarankan adalah menerapkan Singletons dengan hanya membuat tipe enum dengan satu elemen:

// Enum singleton - the preferred approach
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}

Pendekatan ini secara fungsional setara dengan pendekatan lapangan publik, kecuali bahwa itu lebih ringkas, menyediakan mesin serialisasi secara gratis, dan memberikan jaminan kuat terhadap berbagai instantiasi, bahkan dalam menghadapi serialisasi canggih atau serangan refleksi.

Sementara pendekatan ini belum diadopsi secara luas, tipe enum elemen tunggal adalah cara terbaik untuk menerapkan singleton.

ekostadinov
sumber
Saya pikir, untuk membuat singleton dapat diuji, Anda dapat menggunakan pola strategi dan membuat semua tindakan singleton melalui strategi. Jadi, Anda dapat memiliki satu strategi "produksi" dan satu strategi "pengujian" ...
Erk
9

Seperti semua instance enum, Java instantiate setiap objek ketika kelas dimuat, dengan beberapa jaminan bahwa itu instantiated tepat sekali per JVM . Pikirkan INSTANCEdeklarasi sebagai bidang final statis publik: Java akan instantiate objek saat kelas pertama kali disebut.

Mesin virtual dibuat selama inisialisasi statis, yang didefinisikan dalam Spesifikasi Bahasa Jawa, bagian 12.4 .

Untuk apa nilainya, Joshua Bloch menggambarkan pola ini secara rinci sebagai item 3 dari Java Edisi Kedua yang Efektif .

Jeff Bowman
sumber
6

Karena Pola Singleton adalah tentang memiliki konstruktor pribadi dan memanggil beberapa metode untuk mengendalikan instantiasi (seperti beberapa getInstance), dalam Enums kita sudah memiliki konstruktor pribadi implisit.

Saya tidak tahu persis bagaimana JVM atau wadah mengontrol contoh kami Enums, tetapi tampaknya sudah menggunakan implisit Singleton Pattern, bedanya adalah kami tidak memanggil getInstance, kami hanya memanggil Enum.

Bruno Franco
sumber
3

Seperti, sampai batas tertentu, telah disebutkan sebelumnya, enum adalah kelas java dengan kondisi khusus yang definisinya harus dimulai dengan setidaknya satu "konstanta enum".

Selain itu, dan bahwa enum tidak dapat diperpanjang atau digunakan untuk memperluas kelas lain, enum adalah kelas seperti kelas apa pun dan Anda menggunakannya dengan menambahkan metode di bawah definisi konstan:

public enum MySingleton {
    INSTANCE;

    public void doSomething() { ... }

    public synchronized String getSomething() { return something; }

    private String something;
}

Anda mengakses metode singleton di sepanjang baris ini:

MySingleton.INSTANCE.doSomething();
String something = MySingleton.INSTANCE.getSomething();

Penggunaan enum, alih-alih kelas, adalah, seperti yang telah disebutkan dalam jawaban lain, kebanyakan tentang instantiasi single-safe thread dan jaminan bahwa itu akan selalu hanya satu salinan.

Dan, mungkin, yang paling penting, bahwa perilaku ini dijamin oleh JVM itu sendiri dan spesifikasi Java.

Berikut adalah bagian dari spesifikasi Java tentang bagaimana beberapa instance dari instance enum dicegah:

Tipe enum tidak memiliki instance selain yang didefinisikan oleh konstanta enumnya. Ini adalah kesalahan waktu kompilasi untuk mencoba secara eksplisit instantiate tipe enum. Metode klon terakhir dalam Enum memastikan bahwa konstanta enum tidak pernah dapat dikloning, dan perlakuan khusus oleh mekanisme serialisasi memastikan bahwa instance duplikat tidak pernah dibuat sebagai hasil deserialisasi. Instansiasi reflektif dari tipe enum dilarang. Bersama-sama, keempat hal ini memastikan bahwa tidak ada contoh jenis enum ada di luar yang didefinisikan oleh konstanta enum.

Yang perlu diperhatikan adalah bahwa setelah instantiasi masalah keamanan thread harus ditangani seperti di kelas lain dengan kata kunci yang disinkronkan dll.

Erk
sumber