Có thể sử dụng == trên enums trong Java không?


111

Có thể sử dụng ==trên enums trong Java không hay tôi cần phải sử dụng .equals()? Trong thử nghiệm của tôi, ==luôn hoạt động, nhưng tôi không chắc liệu mình có được đảm bảo về điều đó hay không. Đặc biệt, không có .clone()phương thức nào trên enum, vì vậy tôi không biết liệu có thể lấy một enum mà .equals()sẽ trả về một giá trị khác không ==.

Ví dụ, điều này có ổn không:

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

Hay tôi cần viết nó theo cách này:

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


@assylias câu hỏi này đến đầu tiên. Có lẽ gắn cờ cho ♦ sự chú ý, vì tôi không thực sự chắc chắn liệu cả hai có nên được hợp nhất hay không.
Matt Ball

@MattBall Tôi nghĩ câu trả lời cho câu hỏi trích dẫn JLS của bạn là câu trả lời tốt nhất, đó là lý do tại sao tôi chọn đóng câu trả lời này.
assylias

Câu trả lời:


149

Chỉ 2 xu của tôi: Đây là mã cho Enum.java, được xuất bản bởi Sun và một phần của 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
Cảm ơn! Tôi đoán nếu tôi chỉ nghĩ bước vào .equals () bằng trình biên dịch, tôi sẽ thấy điều này ...
Kip

77

Có, == ổn - đảm bảo chỉ là một tham chiếu duy nhất cho mỗi giá trị.

Tuy nhiên, có một cách tốt hơn để viết phương pháp vòng của bạn:

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

Một cách thậm chí còn tốt hơn để làm điều đó là đặt chức năng bên trong chính enum, vì vậy bạn chỉ cần gọi roundingMode.round(someValue). Điều này là trung tâm của các enum Java - chúng là các enum hướng đối tượng , không giống như các "giá trị được đặt tên" được tìm thấy ở những nơi khác.

CHỈNH SỬA: Thông số kỹ thuật không rõ ràng lắm, nhưng phần 8.9 nêu rõ:

Phần thân của kiểu enum có thể chứa hằng số enum. Hằng số enum định nghĩa một thể hiện của kiểu enum. Một kiểu enum không có trường hợp nào khác với những trường hợp được định nghĩa bởi hằng số enum của nó.


Tôi rất muốn nhận lời của bạn về điều đó, nhưng nếu bạn có thể cung cấp liên kết đến một số tài liệu chính thức thì sẽ tốt hơn ...
Kip

switch không hữu ích khi có nhiều chồng chéo giữa các trường hợp khác nhau. Ngoài ra, RoundingMode là một phần của java.math, vì vậy tôi không thể thêm một phương thức vào nó.
Kip

2
Ồ-- và bạn nghi ngờ Jon Skeet? Bạn đã không ở đây lâu lắm rồi;)
Joel Coehoorn

enums trong câu lệnh switch? Không biết rằng điều đó là có thể. Tôi sẽ phải thử nó vào một ngày nào đó.
luiscubal

Đóng gói logic trong enums sử dụng các phương thức trừu tượng là sức mạnh thực sự của enums. Nó làm cho mã của bạn mạnh mẽ hơn nhiều; khi bạn thêm một giá trị enum mới trong tương lai, trình biên dịch sẽ buộc bạn triển khai logic có liên quan, bạn không cần phải nhớ thêm trường hợp vào nhiều câu lệnh switch.
Andrew Swan

13

Có, giống như thể bạn đã tạo các thể hiện singleton cho mỗi giá trị trong enum:

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

  Private RoundingMode () {
    // phạm vi riêng tư ngăn chặn bất kỳ kiểu con nào bên ngoài lớp này
  }
}

Tuy nhiên , enumcấu trúc mang lại cho bạn nhiều lợi ích khác nhau:

  • Mỗi thể hiện toString () in tên được cung cấp trong mã.
  • (Như đã đề cập trong một bài đăng khác,) một biến kiểu enum có thể được so sánh với các hằng số bằng cách sử dụng switch-casecấu trúc điều khiển.
  • Tất cả các giá trị trong kiểu liệt kê có thể được truy vấn bằng cách sử dụng valuestrường được 'tạo' cho mỗi kiểu enum
  • Dưới đây là một trong những so sánh nhận dạng wrt lớn: các giá trị enum tồn tại được tuần tự hóa mà không cần sao chép.

Việc đăng nhiều kỳ là một gotchya lớn. Nếu tôi sử dụng mã ở trên thay vì enum, đây là cách bình đẳng danh tính sẽ hoạt động:

RoundingMode gốc = RoundingMode.HALF_UP;
khẳng định (RoundingMode.HALF_UP == gốc); // vượt qua

ByteArrayOutputStream baos = new ByteArrayOutputStream ();
ObjectOutputStream oos = new ObjectOutputStream (baos);
oos.writeObject (bản gốc);
oos.flush ();

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

khẳng định (RoundingMode.HALF_UP == deserialized); // thất bại
khẳng định (RoundingMode.HALF_EVEN == deserialized); // thất bại

Bạn có thể giải quyết vấn đề này mà không cần enum, bằng cách sử dụng một kỹ thuật liên quan đến writeReplacereadResolve, (xem http: //java.sun.com/j2se/1.4.2/docs/api/java/io/Serializable.html ) ...

Tôi đoán vấn đề là - Java không cho phép bạn sử dụng danh tính các giá trị enum để kiểm tra tính bình đẳng; đó là một thực hành được khuyến khích.


1
lỗi tuần tự hóa đã được sửa. bug.sun.com/bugdatabase/view_bug.do?bug_id=6277781
David I.

@DavidI. Cảm ơn các cập nhật. Đó là một lỗi rất đáng lo ngại, và thật tốt khi biết!
Dilum Ranatunga

1
@DilumRanatunga Lúc đầu, tôi nghĩ điều này sẽ ảnh hưởng đến tôi, nhưng chúng dường như hoạt động tốt sau khi chuyển chúng qua kết nối RMI.
David I.


6

Đây là một số mã độc mà bạn có thể thấy thú vị. : 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));
}

1
@Peter, bạn có thể vui lòng bao gồm các lần nhập mã này không? Cound không tìm thấy Unsafe.class.
rumman0786

3

Enums là một nơi tuyệt vời để làm kẹt mã đa hình.

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
Có nhưng tôi không sở hữu java.math.RoundsMode, vì vậy tôi không thể thực hiện việc này trong trường hợp của mình.
Kip 11/02/09


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.