Câu hỏi So sánh các thành viên enum Java: == hoặc equals ()?


Tôi biết rằng các enums Java được biên dịch thành các lớp với các nhà xây dựng riêng và một nhóm các thành viên tĩnh công cộng. Khi so sánh hai thành viên của một enum đã cho, tôi đã luôn sử dụng .equals(), ví dụ.

public useEnums(SomeEnum a)
{
    if(a.equals(SomeEnum.SOME_ENUM_VALUE))
    {
        ...
    }
    ...
}

Tuy nhiên, tôi chỉ gặp một số mã sử dụng toán tử bằng == thay vì .equals ():

public useEnums2(SomeEnum a)
{
    if(a == SomeEnum.SOME_ENUM_VALUE)
    {
        ...
    }
    ...
}

Toán tử nào tôi nên sử dụng?


1421
2017-11-17 17:26


gốc


Tôi chỉ tình cờ gặp một câu hỏi rất giống nhau: stackoverflow.com/questions/533922/… - Matt Ball
Tôi ngạc nhiên rằng trong tất cả các câu trả lời (đặc biệt là câu trả lời từ polygenelubricants giải thích chi tiết lý do tại sao == works) một lợi ích lớn khác của == không được đề cập: các đối tượng). Với bằng nó dẫn người ta nghĩ rằng bằng cách nào đó có thể có nhiều trường hợp của cùng một enum 'thay thế' nổi xung quanh. - Stuart Rossiter


Các câu trả lời:


Cả hai đều đúng về mặt kỹ thuật. Nếu bạn nhìn vào mã nguồn cho .equals(), nó chỉ đơn giản là ==.

tôi sử dụng ==Tuy nhiên, vì điều đó sẽ không an toàn.


1278
2017-11-17 17:29



Cá nhân, tôi không thích 'an toàn null' được đề cập như là một lý do để sử dụng ==. - Nivas
@ Nivas: tại sao không? Bạn có thích lo lắng về thứ tự của các đối số của bạn (mà chỉ áp dụng để so sánh các hằng số từ bên trái, như trong câu trả lời của Pascal)? Bạn có luôn luôn kiểm tra xem một giá trị không phải là null trước đây không .equals()ing nó? Tôi biết là không. - Matt Ball
Hãy nhớ rằng khi đọc mã của bạn, "==" có thể xuất hiện không chính xác cho người đọc cho đến khi anh ta / cô ấy đi và nhìn vào nguồn cho loại liên quan, sau đó thấy nó là một enum. Theo nghĩa đó, nó ít mất tập trung để đọc ".equals ()". - Kevin Bourrillion
@ Kevin - nếu bạn không sử dụng một IDE cho phép bạn xem các loại trực tiếp khi xem nguồn, bạn đang lừa dối chính mình - không phải là một vấn đề với một IDE thích hợp. - MetroidFan2002
Đôi khi mã được tái cấu trúc và có thể là một enum được thay thế bởi một lớp, tại thời điểm đó == không còn được bảo đảm là đúng, trong khi bằng nhau tiếp tục làm việc liên tục. Vì lý do này, bằng rõ ràng là một sự lựa chọn tốt hơn. Nếu bạn muốn an toàn null (vì bạn làm việc trên một cơ sở mã không được viết để giảm thiểu việc sử dụng các null), có các đối tượng Apache hoặc các đối tượng của Guava # bằng nhau (hoặc bạn có thể cuộn của riêng bạn). Tôi thậm chí sẽ không đề cập đến từ "hiệu suất" vì sợ rằng ai đó đã tống tiền tôi vào một cuốn sách Donald Knuth. - laszlok


Có thể == được sử dụng trên enum?

Có: enums có các điều khiển mẫu chặt chẽ cho phép bạn sử dụng == để so sánh các trường hợp. Đây là sự bảo đảm được cung cấp bởi đặc tả ngôn ngữ (nhấn mạnh bởi tôi):

JLS 8.9 Enums

Một kiểu enum không có trường hợp nào khác ngoài các cá thể được xác định bởi hằng số enum của nó.

Đó là một lỗi biên dịch thời gian để cố gắng để nhanh chóng khởi tạo một loại enum. Các final clone phương pháp trong Enum đảm bảo rằng enum hằng số không bao giờ có thể được nhân bản, và việc xử lý đặc biệt bởi cơ chế tuần tự hóa đảm bảo rằng các cá thể trùng lặp không bao giờ được tạo ra như là kết quả của quá trình deserialization. Phản ứng tức thời của các loại enum bị cấm. Cùng với nhau, bốn điều này đảm bảo rằng không có trường hợp nào của enum loại tồn tại ngoài những định nghĩa được xác định bởi enum hằng số.

Bởi vì chỉ có một ví dụ của mỗi enum không thay đổi, nó được phép sử dụng == nhà điều hành thay cho equals phương pháp khi so sánh hai tham chiếu đối tượng nếu nó được biết rằng ít nhất một trong số chúng đề cập đến enum không thay đổi. (Các equals phương pháp trong Enum là một final phương pháp chỉ đơn thuần gọi super.equals trên đối số của nó và trả về kết quả, do đó thực hiện so sánh danh tính.)

Đảm bảo này đủ mạnh để Josh Bloch đề xuất, nếu bạn nhấn mạnh vào việc sử dụng mẫu đơn, cách tốt nhất để thực hiện nó là sử dụng một phần tử đơn enum (xem: Hiệu quả Java phiên bản 2, Mục 3: Thực thi thuộc tính singleton với một hàm tạo riêng hoặc một kiểu enum; cũng thế Chủ đề an toàn trong Singleton)


Sự khác biệt giữa == và equals?

Như một lời nhắc nhở, nó cần phải được nói rằng nói chung, == KHÔNG phải là một sự thay thế khả thi đối với equals. Tuy nhiên, khi đó (như với enum), có hai sự khác biệt quan trọng cần xem xét:

== không bao giờ ném NullPointerException

enum Color { BLACK, WHITE };

Color nothing = null;
if (nothing == Color.BLACK);      // runs fine
if (nothing.equals(Color.BLACK)); // throws NullPointerException

== có thể kiểm tra tính tương thích kiểu tại thời gian biên dịch

enum Color { BLACK, WHITE };
enum Chiral { LEFT, RIGHT };

if (Color.BLACK.equals(Chiral.LEFT)); // compiles fine
if (Color.BLACK == Chiral.LEFT);      // DOESN'T COMPILE!!! Incompatible types!

Nên == được sử dụng khi áp dụng?

Bloch đề cập cụ thể rằng các lớp học bất biến có quyền kiểm soát thích hợp đối với các trường hợp của họ có thể đảm bảo cho khách hàng của họ rằng == có thể sử dụng được. enum được đề cập cụ thể để minh họa.

Mục 1: Xem xét các phương pháp nhà máy tĩnh thay vì các nhà thầu

[...] nó cho phép một lớp bất biến để đảm bảo rằng không có hai trường hợp bằng nhau tồn tại: a.equals(b)nếu và chỉ nếu a==b. Nếu một lớp đảm bảo điều này, thì khách hàng của nó có thể sử dụng == toán tử thay vì equals(Object) phương pháp, có thể dẫn đến hiệu suất được cải thiện. Enum loại cung cấp bảo lãnh này.

Tóm lại, các đối số để sử dụng == trên enum là:

  • Nó hoạt động.
  • Nó nhanh hơn.
  • Nó an toàn hơn vào thời gian chạy.
  • Nó an toàn hơn ở thời gian biên dịch.

964
2018-05-30 04:38



Câu trả lời hay nhưng ... 1. Nó hoạt động : đã đồng ý 2. Nó nhanh hơn : chứng minh nó cho enums :) 3. Nó an toàn hơn vào thời gian chạy : Color nothing = null; IMHO là một lỗi và nên được cố định, enum của bạn có hai giá trị, không phải ba (xem bình luận của Tom Hawtin) 4. Nó an toàn hơn vào thời gian biên dịch : Được rồi nhưng equals vẫn sẽ trở lại false. Cuối cùng, tôi không mua nó, tôi thích equals() cho khả năng đọc (xem bình luận của Kevin). - Pascal Thivent
Đó là một dấu đen chống lại Java mà một số người nghĩ rằng foo.equals(bar) dễ đọc hơn foo == bar - Bennett McElwee
Nó hoạt động: Cho đến khi bạn cần phải thay thế một enum với một lớp như là một phần của việc tái cấu trúc. Chúc may mắn tìm tất cả các mã bằng cách sử dụng == mà phá vỡ. Nó nhanh hơn: Ngay cả khi đúng (có vấn đề), có báo cáo Knuth về tối ưu hóa sớm. An toàn hơn vào thời gian chạy: "An toàn" không phải là từ đầu tiên lưu ý nếu việc sử dụng == là điều duy nhất tách bạn khỏi NullPointerException. An toàn hơn vào thời gian biên dịch: Không thực sự. Nó có thể bảo vệ bạn khỏi sai lầm khá cơ bản so với các loại sai, nhưng một lần nữa, nếu đó là cách lập trình ngu ngốc của bạn, "an toàn" là một điều bạn không. - laszlok
"nếu đó là cách những người lập trình câm của bạn," an toàn "là điều bạn không làm." - Tôi không đồng ý với câu nói này. Đây là những gì loại kiểm tra là tất cả về: ngăn chặn "câm" những sai lầm tại thời gian biên dịch, do đó, ngay cả trước khi mã được chạy. Ngay cả những người thông minh cũng làm những sai lầm ngớ ngẩn, một số đảm bảo tốt hơn lời hứa. - ymajoros
@laszlok: chắc chắn, nhưng nếu mọi thứ có thể thất bại, họ sẽ chỉ. Có một hàng rào cho một số sai lầm câm luôn luôn là một điều tốt. Có rất nhiều phòng để được sáng tạo cho các lỗi ít tầm thường, hãy để trình biên dịch xử lý này trước khi bạn đã bao giờ cố gắng để chạy mã của bạn. - ymajoros


Sử dụng == để so sánh hai giá trị enum hoạt động vì chỉ có một đối tượng cho mỗi hằng số enum.

Trên một mặt lưu ý, thực sự không cần sử dụng == viết mã không an toàn nếu bạn viết equals() như thế này:

public useEnums(SomeEnum a)
{
    if(SomeEnum.SOME_ENUM_VALUE.equals(a))
    {
        ...
    }
    ...
}

Đây là phương pháp hay nhất được gọi là So sánh các hằng số từ bên trái mà bạn chắc chắn nên làm theo.


72
2017-11-17 17:30



Tôi làm điều này khi .equals()ing chuỗi giá trị, nhưng tôi vẫn nghĩ rằng đó là một cách crappy để viết mã không an toàn. Tôi muốn có một toán tử (hoặc một phương thức, nhưng tôi biết rằng [đối tượng null] .equals sẽ không bao giờ không an toàn trong Java vì cách toán tử dấu chấm hoạt động) luôn an toàn, bất kể theo thứ tự nó được sử dụng. Về mặt ngữ nghĩa, toán tử "bằng" luôn luôn đường đi làm. - Matt Ball
Vâng, vì Java không có toán tử điều hướng an toàn của Groovy (?.), Tôi sẽ tiếp tục sử dụng thực hành này khi so sánh với các hằng số và, TBH, tôi không tìm thấy nó crappy, có lẽ chỉ ít "tự nhiên". - Pascal Thivent
A null enum thường là một lỗi. Bạn đã có điều này tất cả các giá trị có thể, và đây là một số khác! - Tom Hawtin - tackline
Ngoại trừ các giá trị được chú thích rõ ràng là @Nullable, Tôi xem xét So sánh các hằng số từ bên trái một antipattern vì nó lan truyền sự mơ hồ về những giá trị nào có thể hoặc không thể là null. Enum loại hầu như không bao giờ nên @Nullable, do đó, một giá trị null sẽ là một lỗi lập trình; trong trường hợp như vậy, bạn càng ném NPE càng sớm, bạn càng có khả năng sẽ tìm thấy sai lầm lập trình của mình, nơi bạn cho phép nó là rỗng. Vì vậy, "thực hành tốt nhất ... mà bạn chắc chắn nên làm theo" là đồng bằng sai khi nói đến loại enum. - Tobias
So sánh các hằng số từ trái Một cách thực hành tốt nhất như kiểu Yoda tốt nhất là tiếng Anh. - maaartinus


Như những người khác đã nói, cả hai == và .equals() làm việc trong hầu hết các trường hợp. Thời gian biên dịch chắc chắn rằng bạn không so sánh các loại đối tượng hoàn toàn khác nhau mà những người khác đã chỉ ra là hợp lệ và có lợi, tuy nhiên loại lỗi cụ thể so sánh các đối tượng của hai kiểu thời gian biên dịch khác nhau cũng sẽ được FindBugs tìm thấy (và có thể Eclipse / IntelliJ biên dịch thời gian kiểm tra), do đó, trình biên dịch Java tìm thấy nó không thêm nhiều an toàn hơn.

Tuy nhiên:

  1. Thực tế là == không bao giờ ném NPE vào tâm trí của tôi là bất lợi của ==. Không bao giờ cần phải có nhu cầu enum các loại được null, vì bất kỳ trạng thái bổ sung nào mà bạn có thể muốn thể hiện qua null chỉ có thể được thêm vào enum như một ví dụ bổ sung. Nếu nó bất ngờ null, Tôi muốn có NPE hơn là == âm thầm đánh giá sai. Vì vậy, tôi không đồng ý với nó an toàn hơn vào thời gian chạy ý kiến; nó tốt hơn để có được thói quen không bao giờ để cho enum giá trị được @Nullable.
  2. Lập luận rằng == Là nhanh hơn cũng không có thật. Trong hầu hết các trường hợp, bạn sẽ gọi .equals() trên một biến có kiểu thời gian biên dịch là lớp enum, và trong những trường hợp đó trình biên dịch có thể biết rằng điều này giống như == (bởi vì một enum'S equals() phương pháp không thể được ghi đè) và có thể tối ưu hóa chức năng gọi đi. Tôi không chắc liệu trình biên dịch có thực hiện điều này hay không, nhưng nếu không, và hóa ra là một vấn đề hiệu năng trong Java, thì tôi muốn sửa trình biên dịch hơn là có 100.000 lập trình viên Java thay đổi kiểu lập trình của chúng cho phù hợp với một đặc tính hiệu suất của phiên bản trình biên dịch cụ thể.
  3. enums là đối tượng. Đối với tất cả các loại đối tượng khác, so sánh tiêu chuẩn là .equals(), không phải ==. Tôi nghĩ thật nguy hiểm khi tạo một ngoại lệ enums bởi vì bạn có thể vô tình so sánh các đối tượng với == thay vì equals(), đặc biệt nếu bạn refactor một enum vào một lớp không phải enum. Trong trường hợp tái cấu trúc như vậy, Nó hoạt động điểm từ trên là sai. Để thuyết phục bản thân rằng việc sử dụng == là chính xác, bạn cần phải kiểm tra xem giá trị được đề cập là một enum hoặc nguyên thủy; nếu nó không phảienumlớp học, nó sẽ là sai, nhưng dễ dàng bỏ lỡ vì mã vẫn sẽ biên dịch. Trường hợp duy nhất khi sử dụng .equals() sẽ sai nếu các giá trị được đề cập là nguyên thủy; trong trường hợp đó, mã sẽ không biên dịch nên khó bỏ lỡ nhiều hơn. Vì thế, .equals() dễ dàng hơn để xác định là chính xác và an toàn hơn so với các phép tái cấu trúc trong tương lai.

Tôi thực sự nghĩ rằng ngôn ngữ Java nên đã xác định == trên các đối tượng để gọi .equals () trên giá trị tay trái, và giới thiệu một toán tử riêng biệt cho nhận dạng đối tượng, nhưng đó không phải là cách Java được định nghĩa.

Tóm lại, tôi vẫn nghĩ rằng các lập luận có lợi cho việc sử dụng .equals() cho enum loại.


56
2018-02-14 11:59



Vâng, về điểm 3, tôi có thể sử dụng enum như một switch nhắm mục tiêu để chúng có chút đặc biệt. Strings là một chút đặc biệt quá. - CurtainDog
Cú pháp cho các câu lệnh switch không chứa cả == lẫn .equals () vì vậy tôi không thấy điểm của bạn là gì. Điểm của tôi 3.) là về cách dễ dàng mã là để xác minh cho người đọc. Câu lệnh chuyển đổi 'ngữ nghĩa bình đẳng là chính xác miễn là câu lệnh chuyển đổi biên dịch. - Tobias


Đây là một thử nghiệm thời gian thô để so sánh hai:

import java.util.Date;

public class EnumCompareSpeedTest {

    static enum TestEnum {ONE, TWO, THREE }

    public static void main(String [] args) {

        Date before = new Date();
        int c = 0;

        for(int y=0;y<5;++y) {
            for(int x=0;x<Integer.MAX_VALUE;++x) {
                if(TestEnum.ONE.equals(TestEnum.TWO)) {++c;}
                if(TestEnum.ONE == TestEnum.TWO){++c;}              
            }
        }

        System.out.println(new Date().getTime() - before.getTime());
    }   

}

Nhận xét từng cái một. Dưới đây là hai so sánh từ bên trên trong mã byte bị tháo rời:

 21  getstatic EnumCompareSpeedTest$TestEnum.ONE : EnumCompareSpeedTest.TestEnum [19]
 24  getstatic EnumCompareSpeedTest$TestEnum.TWO : EnumCompareSpeedTest.TestEnum [25]
 27  invokevirtual EnumCompareSpeedTest$TestEnum.equals(java.lang.Object) : boolean [28]
 30  ifeq 36

 36  getstatic EnumCompareSpeedTest$TestEnum.ONE : EnumCompareSpeedTest.TestEnum [19]
 39  getstatic EnumCompareSpeedTest$TestEnum.TWO : EnumCompareSpeedTest.TestEnum [25]
 42  if_acmpne 48

Việc đầu tiên (bằng) thực hiện một cuộc gọi ảo và kiểm tra boolean trả về từ ngăn xếp. Thứ hai (==) so sánh các địa chỉ đối tượng trực tiếp từ ngăn xếp. Trong trường hợp đầu tiên có nhiều hoạt động hơn.

Tôi chạy thử nghiệm này nhiều lần với cả hai IF một tại một thời điểm. Các "==" là bao giờ nên nhanh hơn một chút.


12
2017-10-14 21:19



Tôi nghi ngờ rằng dự đoán nhánh của trình biên dịch có thể làm cho thử nghiệm này không hợp lệ. Một trình biên dịch thông minh sẽ nhận ra rằng cả hai câu lệnh if sẽ luôn luôn đánh giá sai và do đó tránh thực hiện nhiều so sánh. Một trình biên dịch thực sự thông minh thậm chí có thể nhận ra rằng các giá trị trong vòng y và x sẽ không ảnh hưởng đến kết quả và tối ưu hóa toàn bộ điều ... - Darrel Hoffman
Nó có thể, nhưng chắc chắn là không. Tôi hiển thị bytecode do trình biên dịch tạo ra thực tế trong câu trả lời của tôi. - ChrisCantrell
cả hai đều bối rối. :-) Chi nhánh dự đoán hoạt động vào thời gian chạy nên mã byte khác nhau không phải là siêu liên quan. Tuy nhiên, trong trường hợp này dự đoán nhánh sẽ giúp cả hai các trường hợp như nhau. - vidstige
Ai quan tâm? Trừ khi bạn đang so sánh enums hàng ngàn lần bên trong một vòng lặp trong một trò chơi video, thêm nano giây là vô nghĩa. - user949300
Bytecode là khá liên quan đến hiệu suất trừ khi bạn đang buộc chế độ giải nghĩa. Viết một JMH điểm chuẩn để thấy hiệu suất giống hệt nhau. - maaartinus


Trong trường hợp enum cả hai đều đúng và đúng !!


10
2017-11-17 17:28





Tôi thích sử dụng == thay vì equals:

Lý do khác, ngoài những người khác đã được thảo luận ở đây, là bạn có thể giới thiệu một lỗi mà không nhận ra nó. Giả sử bạn có enums này là chính xác giống nhau nhưng trong pacakges tách biệt (nó không phổ biến, nhưng nó có thể xảy ra):

Enum đầu tiên:

package first.pckg

public enum Category {
    JAZZ,
    ROCK,
    POP,
    POP_ROCK
}

Thứ hai enum:

package second.pckg

public enum Category {
    JAZZ,
    ROCK,
    POP,
    POP_ROCK
}

Sau đó giả sử bạn sử dụng các bằng như tiếp theo trong item.category đó là first.pckg.Category nhưng bạn nhập enum thứ hai (second.pckg.Category) thay vào đó đầu tiên mà không nhận ra nó:

import second.pckg.Category;
...

Category.JAZZ.equals(item.getCategory())

Vì vậy, bạn sẽ nhận được allways false do là một enum khác nhau mặc dù bạn mong đợi đúng bởi vì item.getCategory() Là JAZZ. Và có thể hơi khó nhìn thấy.

Vì vậy, nếu bạn sử dụng toán tử == bạn sẽ gặp lỗi biên dịch:

toán tử == không thể được áp dụng cho "second.pckg.Category", "first.pckg.Category"

import second.pckg.Category; 
...

Category.JAZZ == item.getCategory() 

10
2018-04-21 06:47





Sử dụng bất kỳ thứ gì khác ngoài == để so sánh hằng số enum là vô nghĩa. Nó giống như so sánh class đối tượng với equals - đừng làm thế!

Tuy nhiên, đã có một lỗi khó chịu (BugId 6277781) trong Sun JDK 6u10 và trước đó có thể thú vị vì lý do lịch sử. Lỗi này ngăn cản việc sử dụng hợp lý == trên enumsialized enums, mặc dù điều này được cho là một phần của một trường hợp góc.


4
2017-10-26 20:40





Enums là các lớp trả về một cá thể (ví dụ như singletons) cho mỗi hằng số liệt kê được khai báo bởi public static final field (không thay đổi) để == nhà điều hành có thể được sử dụng để kiểm tra sự bình đẳng của họ thay vì sử dụng equals() phương pháp


4
2017-11-07 15:21





Tóm lại, cả hai đều có ưu và nhược điểm.

Một mặt, nó có lợi thế để sử dụng ==, như được mô tả trong các câu trả lời khác.

Mặt khác, nếu bạn vì lý do nào đó, hãy thay thế các enums bằng một cách tiếp cận khác (các cá thể lớp bình thường), đã sử dụng == cắn bạn. (BTDT.)


1
2018-06-04 10:42



Điều này là không chính xác. equals() không thể bị ghi đè lên các lớp enum; nó là một final phương pháp. Điều đó sẽ đánh bại toàn bộ mục đích của việc sử dụng một enum. stackoverflow.com/a/2964760/139010 - Matt Ball
@MattBall Cảm ơn. Tôi nên thử nó. Tôi đã viết lại câu trả lời. - glglgl