Java의 열거 형에 ==를 사용해도됩니까?


111

그것은 사용하기 OK인가 ==자바 열거 형에, 또는 내가 사용해야합니까 .equals()? 내 테스트에서는 ==항상 작동하지만 그것이 보장되는지 확실하지 않습니다. 특히, 어떤이없는 .clone()나는 그것을하는 열거 얻을 수 있는지 알고하지 않도록, 열거의 방법 .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이 질문이 먼저 나왔습니다. 두 가지가 병합되어야하는지 확실하지 않기 때문에 ♦ 관심을 끌 수 있습니다.
매트 볼

@MattBall JLS를 인용 한 귀하의 질문에 대한 답변이 최고의 답변이라고 생각합니다. 이것이 제가이 질문을 종료하기로 선택한 이유입니다.
assylias

답변:


149

2 센트 만 : 다음은 Sun에서 게시 한 Enum.java의 코드와 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 ()에 들어 가려고 생각했다면 이걸 보았을 것입니다.
Kip

77

예, == 괜찮습니다-각 값에 대한 단일 참조가 보장됩니다.

그러나 라운드 메서드를 작성하는 더 좋은 방법이 있습니다.

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

나은 그것을하는 방법은 열거 자체 내에서 기능을 넣어, 그래서 당신은 단지 호출 할 수 있습니다 roundingMode.round(someValue). 이것은 Java 열거 형의 핵심입니다 . 다른 곳에서 발견되는 "명명 된 값"과는 달리 객체 지향 열거 형입니다.

편집 : 사양은 명확하지 않지만 섹션 8.9 는 다음과 같이 말합니다.

열거 형 유형의 본문에는 열거 형 상수가 포함될 수 있습니다. 열거 형 상수는 열거 형 유형의 인스턴스를 정의합니다. 열거 형에는 열거 형 상수에 의해 정의 된 것 외에 다른 인스턴스가 없습니다.


나는 당신의 말을 받아들이고 싶지만 더 나은 공식 문서에 대한 링크를 제공 할 수 있다면 ...
Kip

스위치는 서로 다른 케이스 사이에 겹치는 부분이 많을 때 유용하지 않습니다. 또한 RoundingMode는 java.math의 일부이므로 여기에 메서드를 추가 할 수 없습니다.
Kip

2
오-그리고 존 스키트를 의심하십니까? 당신은 여기에 아주 오래 있지 않았습니다;)
Joel Coehoorn

switch 문에 열거 형? 그것이 가능한지 몰랐습니다. 나는 언젠가 그것을 시도해야 할 것입니다.
luiscubal

추상 메서드를 사용하여 열거 형 내에서 논리를 캡슐화하는 것은 열거 형의 진정한 힘입니다. 코드가 훨씬 더 강력 해집니다. 나중에 새 enum 값을 추가하면 컴파일러가 관련 논리를 구현하도록 강제하므로 여러 switch 문에 케이스를 추가하는 것을 기억할 필요가 없습니다.
Andrew Swan

13

예, 마치 열거 형의 각 값에 대해 싱글 톤 인스턴스를 만든 것과 같습니다.

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

  private RoundingMode () {
    // 전용 범위는이 클래스 외부의 모든 하위 유형을 방지합니다.
  }
}

그러나enum구조는 당신에게 다양한 혜택을 제공합니다 :

  • 각 인스턴스의 toString ()은 코드에 지정된 이름을 인쇄합니다.
  • (다른 게시물에서 언급했듯이) enum 유형의 변수는 switch-case제어 구조를 사용하여 상수와 비교할 수 있습니다 .
  • 열거 형의 모든 값은 values각 열거 형 유형에 대해 '생성 된'필드를 사용하여 쿼리 할 수 ​​있습니다.
  • 다음은 큰 wrt ID 비교입니다. 열거 형 값은 복제없이 직렬화를 유지합니다.

연재는 큰 고치 야. 열거 형 대신 위의 코드를 사용하는 경우 ID 평등이 작동하는 방식은 다음과 같습니다.

RoundingMode 원본 = RoundingMode.HALF_UP;
assert (RoundingMode.HALF_UP == 원본); // 통과

ByteArrayOutputStream baos = 새로운 ByteArrayOutputStream ();
ObjectOutputStream oos = 새로운 ObjectOutputStream (baos);
oos.writeObject (원본);
oos.flush ();

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

assert (RoundingMode.HALF_UP == 역 직렬화 됨); // 실패
assert (RoundingMode.HALF_EVEN == 역 직렬화 됨); // 실패

당신은 할 수 관련된 기술을 사용하여, 열거하지 않고이 문제를 해결 writeReplace하고 readResolve, (참조 http://java.sun.com/j2se/1.4.2/docs/api/java/io/Serializable.html를 ) ...

요점은-Java가 동등성을 테스트하기 위해 열거 형 값의 ID를 사용할 수 있도록하는 것입니다. 권장되는 연습입니다.


1
직렬화 버그가 수정되었습니다. bugs.sun.com/bugdatabase/view_bug.do?bug_id=6277781
David I. '

@DavidI. 업데이트 주셔서 감사합니다. 매우 혼란스러운 버그이며 알아두면 좋습니다!
Dilum Ranatunga

1
@DilumRanatunga 처음에는 이것이 나에게 영향을 미칠 것이라고 생각했지만 RMI 연결을 통해 전달한 후에는 정상적으로 작동하는 것으로 보입니다.
David I.


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이 코드의 가져 오기를 포함 해 주시겠습니까? 찾을 수 없습니다 Unsafe.class.
rumman0786

3

열거 형은 다형성 코드를 방해하는 좋은 장소입니다.

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를 소유하고 있지 않으므로 제 경우에는이 작업을 수행 할 수 없습니다.
Kip


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.