[java] Java 열거 형 멤버 비교 : == 또는 equals ()?

Java 열거 형은 개인 생성자와 많은 공개 정적 멤버가있는 클래스로 컴파일된다는 것을 알고 있습니다. 주어진 열거 형의 두 멤버를 비교할 때 항상 사용했습니다 .equals().

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

그러나 방금 ==.equals () 대신 equals 연산자를 사용하는 코드를 발견했습니다 .

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

어떤 연산자를 사용해야합니까?



답변

둘 다 기술적으로 정확합니다. 에 대한 소스 코드를 살펴보면 .equals()간단히 연기됩니다 ==.

내가 사용 ==하는이 null 안전 할 것 같은, 그러나.


답변

==에 사용 enum?

예 : 열거 형에는 인스턴스 ==를 비교하는 데 사용할 수있는 엄격한 인스턴스 컨트롤이 있습니다 . 언어 사양에서 제공하는 보증은 다음과 같습니다 (강조 표시).

JLS 8.9 열거 형

열거 형에는 열거 형 상수로 정의 된 인스턴스 이외의 인스턴스가 없습니다.

열거 형 유형을 명시 적으로 인스턴스화하려고 시도하면 컴파일 타임 오류입니다. 이 final clone방법을 사용 Enum하면 enum상수를 복제 할 수 없으며 직렬화 메커니즘에 의한 특수 처리를 통해 역 직렬화의 결과로 중복 인스턴스가 작성되지 않습니다. 열거 형 유형의 반사 인스턴스화는 금지됩니다. 이 네 가지를 함께 사용 enum하면 enum상수에 의해 정의 된 것 이상의 유형 인스턴스가 존재하지 않습니다 .

enum상수에는 하나의 인스턴스 만 있기 때문에 두 개 객체 참조 중 하나 이상이 상수 를 나타내는 것으로 알려진 경우 두 객체 참조를 비교할 때 메소드 대신 연산자 를 사용할 수==equalsenum 있습니다. (의 equals방법Enum A는 final그것이 단지 호출 방법에 super.equals따라서 식별 비교를 수행하는 인수 및 리턴 결과에).

이 보장은 Josh Bloch가 권장하는 것보다 강력합니다. 싱글 톤 패턴 사용을 고집하는 경우이를 구현하는 가장 좋은 방법은 단일 요소를 사용하는 것입니다 enum( 유효한 Java 2 판, 항목 3 : 개인 생성자 또는 열거 형 ; Singleton의 스레드 안전성 )


의 차이점은 무엇입니까 ==와는 equals?

다시 말해, 일반적으로에 ==대한 실행 가능한 대안이 아니라고 말해야합니다 equals. 그러나 (와 같은 경우 enum) 고려해야 할 두 가지 중요한 차이점이 있습니다.

== 절대 던지지 NullPointerException

enum Color { BLACK, WHITE };

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

== 컴파일 타임에 형식 호환성 검사 대상

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!

==해당되는 경우 사용해야합니까 ?

Bloch는 인스턴스를 적절히 제어 할 수있는 불변 클래스 ==가 사용 가능한 클라이언트를 보장 할 수 있다고 구체적으로 언급합니다 . enum구체적으로 예시 적으로 언급된다.

항목 1 : 생성자 대신 정적 팩토리 메소드 고려

[…] 그것은 불변 클래스가 두 개의 동일한 인스턴스가 존재하지 않도록 보장합니다 : a.equals(b)if and only if a==b. 클래스가이 보증을하면 클라이언트는 ==대신에 연산자를 사용할 수 있습니다 .equals(Object) 메소드 성능을 향상시킬 수 있습니다. 열거 형은이 보증을 제공합니다.

==on 을 사용하기위한 인수 enum는 다음과 같습니다.

  • 효과가있다.
  • 더 빠릅니다.
  • 런타임에 더 안전합니다.
  • 컴파일 타임에 더 안전합니다.

답변

사용 == 각 열거 형 정수에 대해 하나의 객체가 있기 때문에,이 개 열거 값 작품을 비교.

참고로, ==다음 equals()과 같이 작성하면 실제로 널 안전 코드를 작성하는 데 사용할 필요가 없습니다 .

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

이것은 반드시 왼쪽에서 상수 비교로 알려진 모범 사례 입니다.


답변

다른 사람이 말했듯이, 모두 ==.equals() 대부분의 경우에 작동합니다. 다른 사람들이 지적한 완전히 다른 유형의 객체를 비교하지 않는 컴파일 시간 확실성은 유효하고 유익하지만 FindBugs는 두 가지 다른 컴파일 시간 유형의 객체를 비교하는 특정 버그도 발견 할 수 있습니다 Eclipse / IntelliJ 컴파일 시간 검사)) Java 컴파일러는 안전성을 크게 추가하지 않습니다.

하나:

  1. ==내 마음 속에 NPE 를 절대 던지지 않는다는 사실 은 단점 이다 ==. 를 통해 표현하려는 추가 상태를 추가 인스턴스로 추가 할 수 있기 때문에 enum유형이 될 필요는 거의 없습니다 . 예상치 못한 경우 조용히 거짓으로 평가하는 것보다 NPE가 필요합니다 . 따라서 나는 그것이 런타임 의견에 더 안전하다는 것에 동의하지 않는다 . 값을 절대로 두지 않는 습관을들이는 것이 좋습니다.nullnullenumnull==enum@Nullable
  2. 인수 ==입니다 빨리는 또한 가짜입니다. 대부분의 경우 .equals()컴파일 시간 유형이 enum 클래스 인 변수를 호출 하며,이 경우 컴파일러는 ==( enumequals()메소드를 재정의 할 수 없기 때문에) 와 동일하다는 것을 알고 함수 호출을 최적화 할 수 있습니다 떨어져. 컴파일러가 현재이 작업을 수행하고 있는지 확실하지 않지만 그렇지 않은 경우 Java의 전반적인 성능 문제로 판명되면 10 만 명의 Java 프로그래머가 프로그래밍 스타일을 변경하도록 프로그래밍하는 것보다 컴파일러를 수정하는 것이 좋습니다 특정 컴파일러 버전의 성능 특성
  3. enums객체입니다. 다른 모든 객체 유형의 경우 표준 비교는 .equals()아닙니다 ==. 예외 enums==아닌 객체를 비 대신 클래스로 equals()리팩토링하는 경우 실수 대신 객체를 대신 비교할 수 있기 때문에 예외를 만드는 것이 위험하다고 생각합니다 enum. 이러한 리팩토링의 경우, 위의 작동 지점이 잘못되었습니다. ==올바른 사용법을 확신하려면 문제의 가치 enum가 기본인지 또는 기본 인지 확인해야합니다 . enum클래스 가 아닌 경우 코드가 여전히 컴파일되기 때문에 잘못되었지만 쉽게 놓칠 수 있습니다. 사용하는 유일한 경우.equals()문제의 가치가 원시적이라면 틀릴 것이다. 이 경우 코드가 컴파일되지 않으므로 놓치기가 훨씬 어렵습니다. 따라서 .equals()올바른 것으로 식별하기가 훨씬 쉽고 향후 리팩토링에 대해 더 안전합니다.

실제로 Java 언어는 왼쪽 값에서 .equals ()를 호출하기 위해 Objects에 ==을 정의하고 객체 ID에 대해 별도의 연산자를 도입해야한다고 생각하지만 Java가 정의 된 방식은 아닙니다.

요약하자면, 나는 여전히 논쟁이 유형 에 사용 .equals()하는 것을 선호한다고 생각합니다 enum.


답변

나는 ==대신에 사용하는 것을 선호합니다 equals:

다른 이유는 여기에서 이미 논의한 다른 것들 외에도 버그를 깨닫지 않고 버그를 도입 할 수 있기 때문입니다. 정확히 같지만 분리 된 pacakges에있는이 열거 형이 있다고 가정합니다 (일반적이지는 않지만 발생할 수 있음).

첫 번째 열거 형 :

package first.pckg

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

두 번째 열거 형 :

package second.pckg

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

그런 다음에 다음과 같이 등호를 사용하는 가정 item.category이다 first.pckg.Category그러나 당신은 두 번째 열거 (수입 second.pckg.Category그것을 실현하지 않고 첫 번째 대신) :

import second.pckg.Category;
...

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

당신은에 allways를 얻을 것이다 그래서 false당신은 진정한 때문에 예상하지만 다른 열거입니다 때문 item.getCategory()입니다 JAZZ. 그리고보기 어려울 수 있습니다.

따라서 대신 연산자 ==를 사용하면 컴파일 오류가 발생합니다.

operator ==는 “second.pckg.Category”, “first.pckg.Category”에 적용 할 수 없습니다

import second.pckg.Category;
...

Category.JAZZ == item.getCategory() 


답변

다음은 두 가지를 비교하기위한 조잡한 타이밍 테스트입니다.

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

}

IF를 한 번에 하나씩 주석 처리하십시오. 디스 어셈블 된 바이트 코드에서 위의 두 가지 비교는 다음과 같습니다.

 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

첫 번째 (동일)는 가상 호출을 수행하고 스택에서 리턴 부울을 테스트합니다. 두 번째 (==)는 스택에서 직접 객체 주소를 비교합니다. 첫 번째 경우에는 더 많은 활동이 있습니다.

한 번에 하나씩 두 IF로이 테스트를 여러 번 실행했습니다. “==”이 너무 빠릅니다.


답변

열거 형의 경우 모두 정확하고 옳습니다!