สามารถใช้ == บน enums ใน Java ได้หรือไม่?


111

ใช้==กับ enums ใน Java ได้หรือไม่หรือต้องใช้.equals()? ในการทดสอบของฉันใช้==งานได้เสมอ แต่ฉันไม่แน่ใจว่ารับประกันได้หรือไม่ โดยเฉพาะอย่างยิ่งไม่มี.clone()วิธีการใน enum ดังนั้นฉันจึงไม่รู้ว่าเป็นไปได้หรือไม่ที่จะได้ enum ที่.equals()จะส่งคืนค่าที่แตกต่างจาก==.

ตัวอย่างเช่นนี้ใช้ได้หรือไม่:

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

หรือฉันต้องเขียนด้วยวิธีนี้:

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


@assylias คำถามนี้มาก่อน อาจจะตั้งค่าสถานะสำหรับ♦ความสนใจเนื่องจากฉันไม่แน่ใจจริงๆว่าทั้งสองควรรวมเข้าด้วยกัน
Matt Ball

@MattBall ฉันคิดว่าคำตอบสำหรับคำถามของคุณที่อ้างถึง JLS เป็นคำตอบที่ดีที่สุดนั่นคือเหตุผลที่ฉันเลือกที่จะปิดคำถามนี้
assylias

คำตอบ:


149

แค่ 2 เซ็นต์ของฉัน: นี่คือรหัสสำหรับ Enum.java ซึ่งเผยแพร่โดย Sun และเป็นส่วนหนึ่งของ 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;
    }


}

4
ขอบคุณ! ฉันเดาว่าฉันเพิ่งคิดที่จะก้าวเข้าสู่ .equals () ด้วยคอมไพเลอร์ฉันจะได้เห็นสิ่งนี้ ...
กีบ

77

ใช่ == ใช้ได้ - รับประกันได้ว่าจะเป็นเพียงข้อมูลอ้างอิงเดียวสำหรับแต่ละค่า

อย่างไรก็ตามมีวิธีที่ดีกว่าในการเขียนวิธีการกลมของคุณ:

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

วิธีที่ดีกว่านั้นคือการใส่ฟังก์ชันไว้ใน enum เองดังนั้นคุณสามารถโทรroundingMode.round(someValue)ได้ สิ่งนี้เข้าสู่หัวใจของ Java enums - เป็นenums เชิงวัตถุซึ่งแตกต่างจาก "ค่าที่ตั้งชื่อ" ที่พบในที่อื่น

แก้ไข: ข้อมูลจำเพาะไม่ชัดเจนมาก แต่มาตรา 8.9ระบุ:

เนื้อความของประเภท enum อาจมีค่าคงที่ของ enum ค่าคงที่ enum กำหนดอินสแตนซ์ของประเภท enum ประเภท enum ไม่มีอินสแตนซ์อื่นนอกเหนือจากที่กำหนดโดยค่าคงที่ของ enum


ฉันชอบที่จะใช้คำของคุณ แต่ถ้าคุณสามารถให้ลิงค์ไปยังเอกสารที่เป็นทางการบางอย่างที่ดีกว่านี้ ...
กีบ

สวิตช์ไม่มีประโยชน์เมื่อมีการทับซ้อนกันระหว่างกรณีต่างๆ นอกจากนี้ RoundingMode ยังเป็นส่วนหนึ่งของ java.math ดังนั้นฉันจึงไม่สามารถเพิ่มเมธอดเข้าไปได้
กีบ

2
โอ้ - และคุณสงสัยว่า Jon Skeet หรือไม่? คุณไม่ได้อยู่ที่นี่นานนัก;)
Joel Coehoorn

enums ในคำสั่ง switch? ไม่ทราบว่าเป็นไปได้ ฉันจะต้องลองดูสักวัน
luiscubal

การห่อหุ้มตรรกะภายใน enums โดยใช้วิธีนามธรรมเป็นพลังที่แท้จริงของ enums ทำให้โค้ดของคุณมีประสิทธิภาพมากขึ้น เมื่อคุณเพิ่มค่า enum ใหม่ในอนาคตคอมไพเลอร์จะบังคับให้คุณใช้ตรรกะที่เกี่ยวข้องคุณไม่จำเป็นต้องจำไว้ว่าต้องเพิ่มกรณีและปัญหาในคำสั่งสวิตช์หลายรายการ
Andrew Swan

13

ใช่ราวกับว่าคุณได้สร้างอินสแตนซ์ซิงเกิลตันสำหรับแต่ละค่าใน enum:

คลาสนามธรรมสาธารณะ RoundingMode {
  RoundingMode สุดท้ายแบบคงที่สาธารณะ HALF_UP = RoundingMode ใหม่ ();
  RoundingMode สุดท้ายแบบคงที่สาธารณะ HALF_EVEN = RoundingMode ใหม่ ();

  RoundingMode ส่วนตัว () {
    // ขอบเขตส่วนตัวป้องกันประเภทย่อยใด ๆ ที่อยู่นอกคลาสนี้
  }
}

แต่ที่enumสร้างให้คุณประโยชน์ต่างๆ:

  • toString () ของแต่ละอินสแตนซ์จะพิมพ์ชื่อที่ระบุในโค้ด
  • (ดังที่กล่าวไว้ในโพสต์อื่น) ตัวแปรชนิด enum สามารถเปรียบเทียบกับค่าคงที่โดยใช้switch-caseโครงสร้างควบคุม
  • คุณสามารถสอบถามค่าทั้งหมดในการแจงนับได้โดยใช้valuesฟิลด์ที่ 'สร้างขึ้น' สำหรับ enum แต่ละประเภท
  • นี่คือการเปรียบเทียบข้อมูลประจำตัว WRT ที่ยิ่งใหญ่:ค่า enum สามารถอยู่รอดได้โดยไม่ต้องโคลน

การทำให้เป็นอนุกรมเป็น gotchya ขนาดใหญ่ ถ้าฉันจะใช้รหัสด้านบนแทน enum นี่คือลักษณะความเท่าเทียมกันของข้อมูลประจำตัว:

RoundingMode เดิม = RoundingMode.HALF_UP;
ยืนยัน (RoundingMode.HALF_UP == เดิม); // ผ่าน

ByteArrayOutputStream baos = ใหม่ ByteArrayOutputStream ();
ObjectOutputStream oos = ObjectOutputStream ใหม่ (baos);
oos.writeObject (ต้นฉบับ);
oos.flush ();

ByteArrayInputStream bais = ใหม่ ByteArrayInputStream (baos.toByteArray ());
ObjectInputStream ois = ObjectInputStream ใหม่ (bais);
RoundingMode deserialized = (RoundingMode) ois.readObject ();

ยืนยัน (RoundingMode.HALF_UP == deserialized); // ล้มเหลว
ยืนยัน (RoundingMode.HALF_EVEN == deserialized); // ล้มเหลว

คุณสามารถแก้ไขปัญหานี้ได้โดยไม่ต้อง enum โดยใช้เทคนิคที่เกี่ยวข้องกับการwriteReplaceและreadResolve(ดูhttp://java.sun.com/j2se/1.4.2/docs/api/java/io/Serializable.html ) ...

ฉันเดาว่าประเด็นคือ - Java ออกไปจากทางที่อนุญาตให้คุณใช้อัตลักษณ์ของค่า enum เพื่อทดสอบความเท่าเทียมกัน เป็นการฝึกที่ได้รับการสนับสนุน


1
ข้อผิดพลาดการทำให้เป็นอนุกรมได้รับการแก้ไขแล้ว bugs.sun.com/bugdatabase/view_bug.do?bug_id=6277781
David I.

@ ดาวิดไอ. ขอบคุณสำหรับการอัพเดท. นั่นเป็นข้อผิดพลาดที่รบกวนมากและน่ารู้!
Dilum Ranatunga

1
@DilumRanatunga ฉันคิดว่าสิ่งนี้จะส่งผลกระทบต่อฉันในตอนแรก แต่ดูเหมือนว่าพวกเขาจะทำงานได้ดีหลังจากผ่านการเชื่อมต่อ RMI
David I.

11

== เปรียบเทียบการอ้างอิงของสองวัตถุ สำหรับ enums จะรับประกันได้ว่าจะมีเพียงอินสแตนซ์เดียวดังนั้นสำหรับสอง enums ที่เหมือนกัน == จะเป็นจริง

อ้างอิง:

http://www.ajaxonomy.com/2007/java/making-the-most-of-java-50-enum-tricks

(ไม่พบสิ่งใดในเอกสาร Sun)


6

นี่คือรหัสชั่วร้ายที่คุณอาจคิดว่าน่าสนใจ : ง

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));
}

1
@Peter คุณกรุณาใส่การนำเข้าของรหัสนี้ได้หรือไม่? Cound Unsafe.classไม่พบ
rumman0786

3

Enums เป็นสถานที่ที่ดีเยี่ยมในการติดขัดรหัสหลายรูปแบบ

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);
}

1
ใช่ แต่ฉันไม่ได้เป็นเจ้าของ java.math.RoundingMode ดังนั้นฉันจึงไม่สามารถทำสิ่งนี้ได้ในกรณีของฉัน
กีบ


0

== โดยทั่วไปไม่เป็นไรและมีข้อดีทั้ง == และ.equals(). โดยส่วนตัวแล้วฉันชอบใช้เสมอ.equals()เมื่อเปรียบเทียบ Objects รวมถึงenums ด้วย ดูการสนทนานี้:

การเปรียบเทียบสมาชิก Java enum: == หรือเท่ากับ ()?

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.