Bolehkah menggunakan == pada enum di Java?

111

Apakah boleh digunakan ==pada enum di Java, atau apakah saya perlu menggunakannya .equals()? Dalam pengujian saya, ==selalu berhasil, tetapi saya tidak yakin apakah saya dijamin akan hal itu. Secara khusus, tidak ada .clone()metode pada enum, jadi saya tidak tahu apakah mungkin mendapatkan enum yang .equals()akan mengembalikan nilai berbeda dari ==.

Misalnya, apakah ini OK:

public int round(RoundingMode roundingMode) {
  if(roundingMode == RoundingMode.HALF_UP) {
    //do something
  } else if (roundingMode == RoundingMode.HALF_EVEN) {
    //do something
  }
  //etc
}

Atau apakah saya perlu menulis seperti ini:

public int round(RoundingMode roundingMode) {
  if(roundingMode.equals(RoundingMode.HALF_UP)) {
    //do something
  } else if (roundingMode.equals(RoundingMode.HALF_EVEN)) {
    //do something
  }
  //etc
}
Tidur
sumber
4
kemungkinan duplikat anggota Membandingkan Java enum: == atau sama dengan ()?
assylias
@assylias pertanyaan ini datang lebih dulu. Mungkin tandai untuk diperhatikan, karena saya tidak begitu yakin apakah keduanya harus digabungkan.
Matt Ball
@MattBall Saya rasa jawaban atas pertanyaan Anda yang mengutip JLS adalah jawaban terbaik, itulah sebabnya saya memilih untuk menutup pertanyaan ini.
assylias

Jawaban:

149

Hanya 2 sen saya: Ini adalah kode untuk Enum.java, seperti yang diterbitkan oleh Sun, dan bagian dari JDK:

public abstract class Enum<E extends Enum<E>>
    implements Comparable<E>, Serializable {

    // [...]

    /**
     * Returns true if the specified object is equal to this
     * enum constant.
     *
     * @param other the object to be compared for equality with this object.
     * @return  true if the specified object is equal to this
     *          enum constant.
     */
    public final boolean equals(Object other) { 
        return this==other;
    }


}
Varkhan
sumber
4
Terima kasih! Saya kira jika saya baru saja berpikir untuk masuk ke .equals () dengan kompiler saya akan melihat ini ...
Kip
77

Ya, == tidak masalah - dijamin hanya ada satu referensi untuk setiap nilai.

Namun, ada cara yang lebih baik untuk menulis metode bulat Anda:

public int round(RoundingMode roundingMode) {
  switch (roundingMode) {
    case HALF_UP:
       //do something
       break;
    case HALF_EVEN:
       //do something
       break;
    // etc
  }
}

Cara yang lebih baik untuk melakukannya adalah dengan meletakkan fungsionalitas di dalam enum itu sendiri, jadi Anda bisa memanggil roundingMode.round(someValue). Ini sampai ke inti enum Java - mereka enum berorientasi objek , tidak seperti "nilai bernama" yang ditemukan di tempat lain.

EDIT: Spesifikasi tidak terlalu jelas, tetapi bagian 8.9 menyatakan:

Badan jenis enum mungkin berisi konstanta enum. Konstanta enum mendefinisikan turunan dari tipe enum. Jenis enum tidak memiliki instance selain yang ditentukan oleh konstanta enumnya.

Jon Skeet
sumber
Saya ingin mengambil kata-kata Anda untuk itu, tetapi jika Anda dapat memberikan tautan ke beberapa dokumentasi resmi yang lebih baik ...
Kip
switch tidak berguna jika ada banyak tumpang tindih antara kasus yang berbeda. Juga, RoundingMode adalah bagian dari java.math, jadi saya tidak bisa menambahkan metode ke dalamnya.
Kip
2
Oh-- dan Anda meragukan Jon Skeet? Anda belum lama berada di sini;)
Joel Coehoorn
enum dalam pernyataan switch? Tidak tahu itu mungkin. Saya harus mencobanya suatu hari nanti.
luiscubal
Mengenkapsulasi logika dalam enum menggunakan metode abstrak adalah kekuatan enum yang sebenarnya. Itu membuat kode Anda jauh lebih kuat; ketika Anda menambahkan nilai enum baru di masa mendatang, compiler akan memaksa Anda untuk mengimplementasikan logika yang relevan, Anda tidak perlu ingat untuk menambahkan case ke beberapa pernyataan switch.
Andrew Swan
13

Ya, ini seolah-olah Anda telah membuat instance tunggal untuk setiap nilai dalam enum:

public abstract class RoundingMode {
  public static final RoundingMode HALF_UP = new RoundingMode ();
  public static final RoundingMode HALF_EVEN = new RoundingMode ();

  private RoundingMode () {
    // private scope mencegah subtipe apapun di luar kelas ini
  }
}

Namun , enumkonstruksinya memberi Anda berbagai manfaat:

  • Tiap instance toString () mencetak nama yang diberikan dalam kode.
  • (Seperti yang disebutkan di posting lain,) variabel tipe enum dapat dibandingkan dengan konstanta menggunakan switch-casestruktur kontrol.
  • Semua nilai dalam enumerasi dapat ditanyakan menggunakan kolom valuesyang 'dihasilkan' untuk setiap jenis enumerasi
  • Inilah perbandingan besar identitas wrt: nilai enum bertahan serialisasi tanpa kloning.

Serialisasi adalah masalah besar. Jika saya menggunakan kode di atas dan bukan enum, berikut ini bagaimana persamaan identitas akan berperilaku:

RoundingMode original = RoundingMode.HALF_UP;
menegaskan (RoundingMode.HALF_UP == asli); // lulus

ByteArrayOutputStream baos = ByteArrayOutputStream () baru;
ObjectOutputStream oos = baru ObjectOutputStream (baos);
oos.writeObject (asli);
oos.flush ();

ByteArrayInputStream bais = ByteArrayInputStream baru (baos.toByteArray ());
ObjectInputStream ois = baru ObjectInputStream (bais);
RoundingMode deserialized = (RoundingMode) ois.readObject ();

menegaskan (RoundingMode.HALF_UP == deserialized); // gagal
menegaskan (RoundingMode.HALF_EVEN == deserialized); // gagal

Anda dapat mengatasi masalah ini tanpa enum, menggunakan teknik yang melibatkan writeReplacedan readResolve, (lihat http://java.sun.com/j2se/1.4.2/docs/api/java/io/Serializable.html ) ...

Saya kira intinya adalah - Java berusaha keras untuk memungkinkan Anda menggunakan identitas enum values ​​untuk menguji kesetaraan; itu adalah praktik yang didorong.

Dilum Ranatunga
sumber
1
bug serialisasi telah diperbaiki. bugs.sun.com/bugdatabase/view_bug.do?bug_id=6277781
David I.
@Tokopedia terima kasih atas pembaruannya. Itu bug yang sangat mengganggu, dan bagus untuk diketahui!
Dilum Ranatunga
1
@DilumRanatunga Saya pikir ini akan memengaruhi saya pada awalnya, tetapi tampaknya berfungsi dengan baik setelah memberikannya melalui koneksi RMI.
David I.
6

Berikut adalah beberapa kode jahat yang mungkin menarik bagi Anda. : D

public enum YesNo {YES, NO}

public static void main(String... args) throws Exception {
    Field field = Unsafe.class.getDeclaredField("theUnsafe");
    field.setAccessible(true);
    Unsafe unsafe = (Unsafe) field.get(null);
    YesNo yesNo = (YesNo) unsafe.allocateInstance(YesNo.class);

    Field name = Enum.class.getDeclaredField("name");
    name.setAccessible(true);
    name.set(yesNo, "YES");

    Field ordinal = Enum.class.getDeclaredField("ordinal");
    ordinal.setAccessible(true);
    ordinal.set(yesNo, 0);

    System.out.println("yesNo " + yesNo);
    System.out.println("YesNo.YES.name().equals(yesNo.name()) "+YesNo.YES.name().equals(yesNo.name()));
    System.out.println("YesNo.YES.ordinal() == yesNo.ordinal() "+(YesNo.YES.ordinal() == yesNo.ordinal()));
    System.out.println("YesNo.YES.equals(yesNo) "+YesNo.YES.equals(yesNo));
    System.out.println("YesNo.YES == yesNo " + (YesNo.YES == yesNo));
}
Peter Lawrey
sumber
1
@ Peter dapatkah Anda menyertakan impor kode ini? Cound tidak ditemukan Unsafe.class.
rumman0786
3

Enum adalah tempat yang tepat untuk memasukkan kode polimorfik.

enum Rounding {
  ROUND_UP {
    public int round(double n) { ...; }
  },
  ROUND_DOWN {
    public int round(double n) { ...; }
  };

  public abstract int round(double n);
}

int foo(Rounding roundMethod) {
  return roundMethod.round(someCalculation());
}

int bar() {
  return foo(Rounding.ROUND_UP);
}
paulmurray
sumber
1
Ya, tetapi saya tidak memiliki java.math.RoundingMode, jadi saya tidak dapat melakukan ini dalam kasus saya.
Kip
0

== umumnya tidak masalah, dan ada keuntungan untuk == dan .equals(). Saya pribadi lebih suka untuk selalu menggunakan .equals()saat membandingkan Objek, termasuk enums. Lihat juga pembahasan ini:

Membandingkan anggota enum Java: == atau sama dengan ()?

Tobias
sumber