Apa keuntungan dari Java enum versus kelas dengan bidang final statis publik?

147

Saya sangat akrab dengan C # tetapi mulai bekerja lebih banyak di Jawa. Saya berharap untuk mengetahui bahwa enum di Jawa pada dasarnya setara dengan yang di C # tetapi ternyata ini tidak terjadi. Awalnya saya senang mengetahui bahwa enum Java dapat berisi banyak data yang tampaknya sangat menguntungkan ( http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html ). Namun, sejak itu saya telah menemukan banyak fitur yang hilang yang sepele di C #, seperti kemampuan untuk dengan mudah menetapkan elemen enum nilai tertentu, dan akibatnya kemampuan untuk mengkonversi integer ke enum tanpa usaha yang layak () yaitu Konversi nilai integer ke Java Enum yang cocok ).

Jadi pertanyaan saya adalah ini: apakah ada manfaat untuk Java enum atas kelas dengan sekelompok bidang final statis publik? Atau apakah itu hanya memberikan sintaks yang lebih kompak?

EDIT: Biarkan saya lebih jelas. Apa manfaat Java enums di atas kelas dengan sekelompok bidang final statis publik dari jenis yang sama ? Sebagai contoh, dalam contoh planet di tautan pertama, apa keuntungan enum dibandingkan kelas dengan konstanta publik ini:

public static final Planet MERCURY = new Planet(3.303e+23, 2.4397e6);
public static final Planet VENUS = new Planet(4.869e+24, 6.0518e6);
public static final Planet EARTH = new Planet(5.976e+24, 6.37814e6);
public static final Planet MARS = new Planet(6.421e+23, 3.3972e6);
public static final Planet JUPITER = new Planet(1.9e+27, 7.1492e7);
public static final Planet SATURN = new Planet(5.688e+26, 6.0268e7);
public static final Planet URANUS = new Planet(8.686e+25, 2.5559e7);
public static final Planet NEPTUNE = new Planet(1.024e+26, 2.4746e7);

Sejauh yang saya tahu, jawaban casablanca adalah satu-satunya yang memuaskan ini.

Craig W
sumber
4
@Bohemian: Ini mungkin bukan duplikat, karena bidang OP hanya disebutkan public static final, yang mungkin diketik nilai dan tidak harus ints.
casablanca
1
@ Shahzeb Sulit. Jelas menggunakan enum alih-alih konstanta string adalah ide yang sangat bagus dan lebih dari dianjurkan. Jenis keamanan, tidak memerlukan fungsi utilitas statis, dan sebagainya .. sama sekali tidak ada alasan untuk menggunakan string.
Voo
1
@ Voo Ya saya tahu akan ada perbedaan pendapat. Dan di sini ada satu dalam 49 detik. Enum itu hebat (dan saya sangat menyukainya dan menggunakannya sangat sering) tetapi jenis keamanan apa yang Anda butuhkan ketika mendeklarasikan konstanta atau menggunakannya. Ini adalah kerja keras untuk membuat enum setiap kali Anda perlu mendeklarasikan konstanta untuk String literal.
Shahzeb
4
@ Shahzeb Jika Anda memiliki variabel tunggal, pastikan menggunakan string, tidak banyak yang bisa terjadi di sini (nilai tunggal agak tidak berguna sebagai parameter). Tetapi perhatikan bahwa kita sedang berbicara tentang konstanta S, jadi sekarang kita berbicara tentang kemungkinan meneruskannya ke fungsi, dll. Apakah kita perlu mengetikkan keselamatan? Ya, tidak, tapi kebanyakan orang menganggap tipe c-style "semuanya adalah void*gaya yang bagus" dan itu bisa menghentikan bug (terutama jika melewati lebih dari satu parameter enum / string!). Juga menempatkan konstanta ke dalam namespace mereka sendiri, dll. Bertentangan dengan itu, tidak ada keuntungan nyata untuk hanya memiliki variabel sederhana.
Voo
3
@ Bohemian: Saya tidak mengerti caranya. Dengan ints, tidak ada jenis keamanan karena seseorang dapat melewati nilai apa pun. Objek yang diketik, di sisi lain, tidak berbeda dengan enum dalam hal keamanan tipe.
casablanca

Jawaban:

78

Secara teknis orang memang bisa melihat enum sebagai kelas dengan sekelompok konstanta yang diketik, dan inilah sebenarnya bagaimana konstanta enum diimplementasikan secara internal. enumNamun menggunakan metode memberi Anda berguna ( Enum javadoc ) yang Anda harus menerapkan sendiri, seperti Enum.valueOf.

casablanca
sumber
14
Ada juga .values()untuk mengulangi daftar nilai.
h3xStream
1
Ini sepertinya jawaban yang tepat, meskipun tidak terlalu memuaskan. Menurut pendapat saya, hampir tidak ada gunanya bagi Java untuk menambahkan dukungan untuk enum hanya untuk sintaks yang lebih ringkas dan akses ke metode Enum.
Craig W
7
@Craig insting Anda benar - ini adalah jawaban yang sangat buruk, karena sepenuhnya merindukan tujuan enum Lihat komentar saya di bawah pertanyaan untuk sebagian alasan mengapa.
Bohemian
1
@ Bohemian: Saya tidak "melewatkan tujuan" enum - saya menggunakannya sepanjang waktu. Lihat tanggapan saya terhadap komentar Anda di atas.
casablanca
1
Metode Enum.valuesOf mengembalikan objek yang sama jika dipanggil dua kali?
Emre Aktürk
104
  1. Ketik keamanan dan keamanan nilai.
  2. Dijamin singleton.
  3. Kemampuan untuk mendefinisikan dan mengganti metode.
  4. Kemampuan untuk menggunakan nilai-nilai dalam switchpernyataan casepernyataan tanpa kualifikasi.
  5. Sequentialization bawaan nilai melalui ordinal().
  6. Serialisasi berdasarkan nama bukan berdasarkan nilai, yang menawarkan tingkat pemeriksaan di masa depan.
  7. EnumSetdan EnumMapkelas.
Marquis dari Lorne
sumber
19
Setelah mengatakan semua itu, setiap kali saya memasukkan kode ke dalam Enum, saya menyesalinya.
Marquis of Lorne
4
Mengapa Anda menyesalinya? Saya tidak pernah ...
glglgl
2
@glglgl Karena itu menempatkan kode khusus aplikasi ke tempat di mana saya merasa itu bukan milik, itu benar-benar hanya mendefinisikan satu set nilai. Jika saya harus melakukannya lagi saya akan memasukkannya dalam salah satu dari banyak switchpernyataan yang merupakan motivasi asli untuk menggunakan Enumsama sekali.
Marquis dari Lorne
72

Tidak ada yang menyebutkan kemampuan untuk menggunakannya dalam switchpernyataan; Saya akan melemparkan itu juga.

Hal ini memungkinkan enum kompleks sewenang-wenang untuk digunakan dengan cara yang bersih tanpa menggunakan instanceof, ifurutan yang berpotensi membingungkan , atau nilai switching non-string / int. Contoh kanonik adalah mesin negara.

Dave Newton
sumber
Bagaimanapun, Anda tidak menyebutkan manfaat apa pun dari enums vs bidang statis, Anda dapat menggunakan cukup banyak jenis dalam pernyataan peralihan dengan bidang statis. Op Butuh perbedaan fungsional atau kinerja yang nyata
Genaut
@ Genaut Manfaatnya adalah bahwa enum memiliki lebih banyak fungsi daripada string atau int-pertanyaannya adalah tentang perbedaan, yang saya berikan. OP sudah mengetahui apa itu enum, dan tidak ada orang lain yang menyebutkan pernyataan pergantian ketika saya memposting ini 4,5 tahun yang lalu, dan setidaknya beberapa orang menemukan itu memberikan info baru ¯_ (ツ) _ / ¯
Dave Newton
44

Keuntungan utama adalah keamanan jenis. Dengan seperangkat konstanta, nilai apa pun dari tipe intrinsik yang sama dapat digunakan, menyebabkan kesalahan. Dengan enum hanya nilai yang berlaku yang dapat digunakan.

Sebagai contoh

public static final int SIZE_SMALL  = 1;
public static final int SIZE_MEDIUM = 2;
public static final int SIZE_LARGE  = 3;

public void setSize(int newSize) { ... }

obj.setSize(15); // Compiles but likely to fail later

vs.

public enum Size { SMALL, MEDIUM, LARGE };

public void setSize(Size s) { ... }

obj.setSize( ? ); // Can't even express the above example with an enum
Jim Garrison
sumber
3
kelas juga merupakan tipe aman ...: / (dengan asumsi medan statis adalah jenis kelas wadah)
h3xStream
Anda masih bisa meneruskannya dengan nilai yang tidak valid tetapi itu akan menjadi kesalahan waktu kompilasi yang jauh lebih mudah dikenali.
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
2
Anda dapat memanggil setSize(null)dalam contoh kedua Anda, tetapi kemungkinan besar gagal lebih cepat daripada kesalahan contoh pertama.
Jeffrey
42

Ada sedikit kebingungan. Ambil Fontcontoh. Ini memiliki konstruktor yang mengambil nama yang FontAnda inginkan, ukurannya dan gayanya ( new Font(String, int, int)). Sampai hari ini saya tidak ingat apakah gaya atau ukuran lebih dulu. Jika Fonttelah menggunakan sebuah enumuntuk semua gaya yang berbeda ( PLAIN, BOLD, ITALIC, BOLD_ITALIC), konstruktor akan terlihat seperti Font(String, Style, int), mencegah kebingungan. Sayangnya, enumtidak ada ketika Fontkelas dibuat, dan karena Java harus mempertahankan kompatibilitas terbalik, kita akan selalu terganggu oleh ambiguitas ini.

Tentu saja, ini hanya sebuah argumen untuk menggunakan enumbukan public static finalkonstanta. Enum juga sempurna untuk lajang dan menerapkan perilaku default sambil memungkinkan penyesuaian selanjutnya (yaitu pola strategi ). Contoh yang terakhir adalah java.nio.file's OpenOptiondan StandardOpenOption: jika pengembang ingin membuat sendiri non-standar nya OpenOption, dia bisa.

Jeffrey
sumber
Kasing 'Font' Anda masih ambigu jika dua enum yang sama diminta. Jawaban kanonik untuk masalah itu adalah apa yang oleh banyak bahasa disebut Parameter Bernama . Itu atau dukungan tanda tangan metode IDE yang lebih baik.
aaaaaa
1
@aaaaaa Saya belum melihat banyak kasus di mana seorang konstruktor akan mengambil dua hal yang sama enumtanpa menggunakan varargs atau a Setuntuk mengambil angka acak dari mereka.
Jeffrey
@aaaaaa Masalah utama dengan parameter bernama itu bergantung pada detail implementasi (nama parameter). Saya bisa membuat antarmuka interface Foo { public void bar(String baz); }. Seseorang membuat sebuah kelas yang memanggil bar: someFoo.bar(baz = "hello");. Saya mengubah tanda tangan Foo::barmenjadi public void bar(String foobar). Sekarang orang yang dipanggil someFooharus mengubah kode mereka jika mereka masih ingin berfungsi.
Jeffrey
Saya tidak ingat melihat tipe enum berturut-turut, tetapi berpikir yang umum adalah DAY_OF_WEEK atau yang serupa. Dan poin bagus tentang antarmuka - tidak memikirkan itu. Secara pribadi saya akan mengambil masalah atas ambiguitas luas yang disebabkan oleh parameter yang tidak disebutkan namanya, yang membutuhkan dukungan IDE yang lebih kuat. Saya mengerti bahwa itu panggilan penilaian, dan bahwa melanggar perubahan API adalah sesuatu yang perlu dipertimbangkan secara serius.
aaaaaa
26

Ada banyak jawaban bagus di sini, tetapi tidak ada yang menyebutkan bahwa ada implementasi yang sangat optimal dari kelas API / interface Collection khusus untuk enum :

Kelas khusus enum ini hanya menerima Enuminstance ( EnumMapsatu - satunya yang menerimaEnum hanya sebagai kunci), dan bila memungkinkan, mereka kembali ke representasi kompak dan manipulasi bit dalam implementasinya.

Apa artinya ini?

Jika Enumjenis kami tidak memiliki lebih dari 64 elemen (sebagian besar Enumcontoh kehidupan nyata akan memenuhi syarat untuk ini), implementasi menyimpan elemen dalam longnilai tunggal , setiap Enumcontoh yang bersangkutan akan dikaitkan dengan sedikit dari panjang 64-bit ini long. Menambahkan elemen ke EnumSethanya dengan menetapkan bit yang tepat ke 1, menghapusnya hanya mengatur bit ke 0. Menguji apakah suatu elemen ada di dalam Sethanya satu uji bitmask! Sekarang kamu harus cinta Enumuntuk ini!

icza
sumber
1
Saya tahu tentang dua ini sebelumnya, tetapi saya baru belajar banyak dari What does this mean?bagian Anda . Saya tahu ada kesenjangan dalam implementasi pada ukuran 64, tapi saya tidak benar-benar tahu mengapa
Christopher Rucinski
15

contoh:

public class CurrencyDenom {
   public static final int PENNY = 1;
 public static final int NICKLE = 5;
 public static final int DIME = 10;
public static final int QUARTER = 25;}

Batasan konstanta java

1) Tidak Ada Jenis Keamanan : Pertama-tama ini bukan tipe-safe; Anda dapat menetapkan nilai int yang valid ke int misalnya 99 meskipun tidak ada koin untuk mewakili nilai itu.

2) Tidak Mencetak Berarti : nilai pencetakan konstanta ini akan mencetak nilai numeriknya alih-alih nama koin yang berarti, misal, ketika Anda mencetak NICKLE, ia akan mencetak "5" alih-alih "NICKLE"

3) Tidak ada namespace : untuk mengakses konstanta currencyDenom kita perlu awalan nama kelas mis. CurrencyDenom.PENNY alih-alih hanya menggunakan PENNY meskipun ini juga dapat dicapai dengan menggunakan impor statis di JDK 1.5

Keuntungan enum

1) Enums di Jawa adalah tipe-safe dan memiliki ruang nama sendiri. Ini berarti enum Anda akan memiliki jenis misalnya "Mata Uang" dalam contoh di bawah ini dan Anda tidak dapat menetapkan nilai apa pun selain yang ditentukan dalam Enum Constants.

public enum Currency {PENNY, NICKLE, DIME, QUARTER};

Currency coin = Currency.PENNY; coin = 1; //compilation error

2) Enum di Jawa adalah tipe referensi seperti kelas atau antarmuka dan Anda dapat menentukan konstruktor, metode dan variabel di dalam java Enum yang membuatnya lebih kuat daripada Enum dalam C dan C ++ seperti yang ditunjukkan dalam contoh berikutnya dari tipe Java Enum.

3) Anda dapat menentukan nilai konstanta enum pada waktu pembuatan seperti yang ditunjukkan pada contoh di bawah ini: public enum Currency {PENNY (1), NICKLE (5), DIME (10), QUARTER (25)}; Tetapi agar ini berfungsi, Anda perlu mendefinisikan variabel anggota dan konstruktor karena PENNY (1) sebenarnya memanggil konstruktor yang menerima nilai int, lihat contoh di bawah ini.

public enum Currency {
    PENNY(1), NICKLE(5), DIME(10), QUARTER(25);
    private int value;

    private Currency(int value) {
            this.value = value;
    }
}; 

Referensi: https://javarevisited.blogspot.com/2011/08/enum-in-java-example-tutorial.html

Maverick
sumber
11

Manfaat pertama enum, seperti yang telah Anda perhatikan, adalah kesederhanaan sintaksis. Tetapi poin utama enum adalah untuk menyediakan seperangkat konstanta terkenal yang, secara default, membentuk rentang dan membantu untuk melakukan analisis kode yang lebih komprehensif melalui pemeriksaan keamanan tipe & nilai.

Atribut enum tersebut membantu programmer dan kompiler. Misalnya, Anda melihat fungsi yang menerima bilangan bulat. Apa artinya bilangan bulat itu? Nilai seperti apa yang bisa Anda berikan? Anda tidak benar-benar tahu segera. Tetapi jika Anda melihat fungsi yang menerima enum, Anda tahu betul semua kemungkinan nilai yang bisa Anda sampaikan.

Untuk kompiler, enum membantu untuk menentukan rentang nilai dan kecuali Anda menetapkan nilai khusus untuk anggota enum, mereka dengan baik berkisar dari 0 dan lebih tinggi. Ini membantu untuk secara otomatis melacak kesalahan dalam kode melalui pemeriksaan keamanan jenis dan banyak lagi. Sebagai contoh, kompiler dapat memperingatkan Anda bahwa Anda tidak menangani semua nilai enum yang mungkin dalam pernyataan switch Anda (yaitu ketika Anda tidak memilikinyadefault kasing dan hanya menangani satu dari nilai N enum). Ini juga memperingatkan Anda ketika Anda mengkonversi bilangan bulat arbitrer menjadi enum karena rentang nilai enum kurang dari bilangan bulat dan yang pada gilirannya dapat memicu kesalahan dalam fungsi yang tidak benar-benar menerima bilangan bulat. Selain itu, membuat tabel lompat untuk sakelar menjadi lebih mudah ketika nilainya dari 0 ke atas.

Ini tidak hanya berlaku untuk Java, tetapi untuk bahasa lain dengan pemeriksaan tipe yang ketat juga. C, C ++, D, C # adalah contoh yang baik.


sumber
4

Enum adalah implictly akhir, dengan konstruktor pribadi, semua nilai-nilainya adalah dari jenis yang sama atau sub-jenis, Anda dapat memperoleh semua nilai dengan menggunakan values(), mendapat nya name()atau ordinal()nilai atau Anda dapat melihat sebuah enum dengan nomor atau nama.

Anda juga dapat mendefinisikan subclass (meskipun secara final dianggap final, sesuatu yang tidak dapat Anda lakukan dengan cara lain)

enum Runner implements Runnable {
    HI {
       public void run() {
           System.out.println("Hello");
       }
    }, BYE {
       public void run() {
           System.out.println("Sayonara");
       }
       public String toString() {
           return "good-bye";
       }
    }
 }

 class MYRunner extends Runner // won't compile.
Peter Lawrey
sumber
4

enum Manfaat:

  1. Enum adalah tipe-aman, bidang statis tidak
  2. Ada sejumlah nilai terbatas (tidak mungkin untuk melewatkan nilai enum yang tidak ada. Jika Anda memiliki bidang kelas statis, Anda dapat membuat kesalahan itu)
  3. Setiap enum dapat memiliki beberapa properti (bidang / getter) yang ditugaskan - enkapsulasi. Juga beberapa metode sederhana: YEAR.toSeconds () atau serupa. Bandingkan: Colors.RED.getHex () dengan Colors.toHex (Colors.RED)

"seperti kemampuan untuk dengan mudah menetapkan elemen enum nilai tertentu"

enum EnumX{
  VAL_1(1),
  VAL_200(200);
  public final int certainValue;
  private X(int certainValue){this.certainValue = certainValue;}
}

"dan akibatnya kemampuan untuk mengubah bilangan bulat menjadi enum tanpa usaha yang layak" Tambahkan metode konversi int ke enum yang melakukan itu. Cukup tambahkan HashMap <Integer, EnumX> statis yang berisi pemetaan java enum .

Jika Anda benar-benar ingin mengonversi ord = VAL_200.ordinal () kembali ke val_200 cukup gunakan: EnumX.values ​​() [ord]

mabn
sumber
3

Perbedaan penting lainnya adalah bahwa kompiler java memperlakukan static finalbidang tipe primitif dan String sebagai literal. Ini berarti konstanta ini menjadi sejajar. Ini mirip dengan C/C++ #definepreprocessor. Lihat pertanyaan SO ini . Ini tidak terjadi dengan enum.

Sayur Jalal Hosseini
sumber
2

Anda mendapatkan waktu kompilasi memeriksa nilai-nilai yang valid ketika Anda menggunakan enum. Lihatlah pertanyaan ini.

zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
sumber
2

Keuntungan terbesar adalah enum Singletons mudah ditulis dan aman utas:

public enum EasySingleton{
    INSTANCE;
}

dan

/**
* Singleton pattern example with Double checked Locking
*/
public class DoubleCheckedLockingSingleton{
     private volatile DoubleCheckedLockingSingleton INSTANCE;

     private DoubleCheckedLockingSingleton(){}

     public DoubleCheckedLockingSingleton getInstance(){
         if(INSTANCE == null){
            synchronized(DoubleCheckedLockingSingleton.class){
                //double checking Singleton instance
                if(INSTANCE == null){
                    INSTANCE = new DoubleCheckedLockingSingleton();
                }
            }
         }
         return INSTANCE;
     }
}

keduanya serupa dan menangani serialisasi sendiri dengan menerapkan

//readResolve to prevent another instance of Singleton
    private Object readResolve(){
        return INSTANCE;
    }

lebih

Premraj
sumber
0

Saya pikir enumtidak bisa final, karena di bawah kap kompiler menghasilkan subclass untuk setiap enumentri.

Informasi lebih lanjut Dari sumber

Sitansu
sumber
Secara internal, mereka tidak final, karena - seperti yang Anda katakan - secara internal dapat subkelas. Tetapi sayangnya, Anda tidak dapat mensubklasifikasikan mereka sendiri, misalnya untuk memperluasnya dengan nilai-nilai sendiri.
glglgl
0

Ada banyak keuntungan enum yang diposting di sini, dan saya membuat enum seperti itu sekarang seperti yang ditanyakan dalam pertanyaan. Tapi saya punya enum dengan 5-6 bidang.

enum Planet{
EARTH(1000000, 312312321,31232131, "some text", "", 12),
....
other planets
....

Dalam kasus-kasus seperti ini, ketika Anda memiliki beberapa bidang dalam enum, sangat sulit untuk memahami nilai mana yang dimiliki bidang mana yang Anda perlukan untuk melihat konstruktor dan bola mata.

Kelas dengan static finalkonstanta dan menggunakan Builderpola untuk membuat objek seperti itu membuatnya lebih mudah dibaca. Tapi, Anda akan kehilangan semua keuntungan lain menggunakan enum, jika Anda membutuhkannya. Salah satu kelemahan dari kelas tersebut adalah, Anda perlu menambahkan Planetobjek secara manual ke list/setdariPlanets.

Aku masih lebih suka enum lebih kelas tersebut, seperti values()berguna dan Anda tidak pernah tahu apakah Anda membutuhkan mereka untuk digunakan dalam switchatau EnumSetatau EnumMapdi masa depan :)

Bikas Katwal
sumber
0

Alasan utama: Enums membantu Anda untuk menulis kode yang terstruktur dengan baik di mana makna parameter semantik jelas dan diketik dengan kuat pada waktu kompilasi - untuk semua alasan yang diberikan jawaban lain.

Quid pro quo: di Jawa di luar kotak, susunan anggota Enum adalah final. Itu biasanya baik karena membantu nilai keamanan dan pengujian, tetapi dalam beberapa situasi itu bisa menjadi kelemahan, misalnya jika Anda memperluas kode dasar yang ada mungkin dari perpustakaan. Sebaliknya, jika data yang sama ada di kelas dengan bidang statis, Anda dapat dengan mudah menambahkan instance baru dari kelas tersebut saat runtime (Anda mungkin juga perlu menulis kode untuk menambahkan ini ke Iterable yang Anda miliki untuk kelas itu). Tetapi perilaku Enums ini dapat diubah: menggunakan refleksi Anda dapat menambahkan anggota baru saat runtime atau mengganti anggota yang sudah ada, meskipun ini mungkin hanya boleh dilakukan dalam situasi khusus di mana tidak ada alternatif: yaitu itu adalah solusi hacky dan dapat menghasilkan masalah yang tidak terduga, lihat jawaban saya diDapatkah saya menambah dan menghapus elemen enumerasi saat runtime di Java .

Radfast
sumber