code

스위치가 열거 형인 경우 기본값의 사용법은 무엇입니까?

codestyles 2020. 12. 6. 21:34
반응형

스위치가 열거 형인 경우 기본값의 사용법은 무엇입니까?


내가 열거 있다고 가정 Color이 개 가능한 값을 : REDBLUE:

public enum Color {
    RED,
    BLUE
}

이제 두 가능한 값에 대한 코드가있는이 열거 형에 대한 switch 문이 있다고 가정합니다.

Color color = getColor(); // a method which returns a value of enum "Color"
switch (color) {
case RED:
   ...
   break;

case BLUE:
   ...
   break;

default:
   break;
}

열거 형의 가능한 값 모두에 대한 코드 블록 default이 있으므로 위 코드에서 사용되는 용도는 무엇 입니까?

코드가 default이런 블록에 어떻게 든 도달하면 예외를 던져야 합니까?

Color color = getColor(); // a method which returns a value of enum "Color"
switch (color) {
case RED:
   ...
   break;

case BLUE:
   ...
   break;

default:
   throw new IllegalArgumentException("This should not have happened");
}

두 번째 예제에서 보여준 것처럼 Exception을 던지는 것이 좋습니다. 빠르게 실패하여 코드의 유지 관리 성을 향상시킵니다.

이 경우 나중에 (아마도 몇 년 후) 열거 형 값을 추가하고 switch 문에 도달하면 즉시 오류를 발견하게됩니다.

기본값이 설정되지 않은 경우 새 열거 형 값을 사용해도 코드가 실행되고 원하지 않는 동작이 발생할 수 있습니다.


다른 대답은 default나중에 새 값이 열거 형에 추가되는 경우 예외를 발생 시키는 분기를 구현해야한다는 점에서 정확 합니다. 그러나 나는 한 단계 더 나아가서 왜 당신이 switch처음에 진술을 사용하고 있는지 의문을 품을 것입니다 .

C ++ 및 C #과 같은 언어와 달리 Java는 Enum 값을 실제 개체로 나타내므로 개체 지향 프로그래밍을 활용할 수 있습니다. 메서드의 목적이 각 색상에 대한 RGB 값을 제공하는 것이라고 가정 해 보겠습니다.

switch (color)
    case RED:
       return "#ff0000";
    ...

글쎄, 틀림없이 각 색상이 RGB 값을 갖기를 원한다면 설명의 일부로 포함해야합니다.

public enum Color
{
    RED("#FF0000"),
    BLUE("#0000FF");

    String rgb;
    public Color(String rgb) {
        this.rgb = rgb;
    }
    public getRgb() { return this.rgb; }
}

이렇게하면 나중에 새 색상을 추가 할 경우 RGB 값을 제공해야합니다. 런타임이 아닌 컴파일 타임에 실패하기 때문에 다른 접근 방식보다 훨씬 빠르게 실패합니다.

필요한 경우 각 색상이 추상 메서드의 고유 한 사용자 지정 구현을 제공하도록하는 것을 포함하여 훨씬 더 복잡한 작업을 수행 할 수 있습니다. Java의 열거 형은 정말 강력하고 객체 지향적이며, 대부분의 경우 처음에 열거 할 필요가 없다는 것을 알았 switch습니다.


스위치 케이스의 컴파일 시간 완전성은 런타임 완전성을 보장하지 않습니다.

이전 버전의 enum에 대해 컴파일 된 switch 문이있는 클래스는 최신 enum 버전 (더 많은 값 포함)으로 실행될 수 있습니다. 이는 라이브러리 종속성의 일반적인 경우입니다.

이와 같은 이유로 컴파일러는 대소 문자 switch없이 default불완전한 것으로 간주합니다 .


작은 프로그램에서는 실제로 사용할 수 없지만 많은 수의 파일과 개발자 사이에서 튀어 나오는 복잡한 시스템을 생각해보십시오 enum. 한 파일에서 정의하고 다른 파일에서 사용하고 나중에 누군가가 값을 추가하는 경우 는 enum업데이트하지 않고 switch문을, 당신은 매우 유용 할 것입니다 ...


당신이 당신의 다양한으로 모든 가능성을 다룬 경우 cases와는 default일어날 수없는,이에 대한 고전적인 사용 사례입니다 주장 :

Color color = getColor(); // a method which returns a value of enum "Color"
switch (color) {
    case RED:
       // ...
       break;

    case BLUE:
       // ...
       break;

    default:
       assert false; // This cannot happen
       // or:
       throw new AssertionError("Invalid Colors enum");
}

IDE 및 기타 정적 린터를 충족하기 위해 종종 // Can't happen또는 같은 주석과 함께 기본 케이스를 no-op으로 남겨 둡니다.// Unreachable

즉, 스위치가 명시 적으로 또는 폴 스루를 통해 가능한 모든 열거 형 값을 처리하는 일반적인 작업을 수행하는 경우 기본 사례는 아마도 프로그래머 오류 일 것입니다.

응용 프로그램에 따라 개발 중에 프로그래머 오류를 방지하기 위해 경우에 따라 어설 션을 넣는 경우가 있습니다. 그러나 이것은 배송 코드의 가치가 제한적입니다 (어설 션이 활성화 된 상태로 배송하지 않는 한).

다시 말하지만 상황에 따라 오류를 던질 수 있습니다. 이것은 실제로 복구 할 수없는 상황이기 때문입니다. 사용자가 할 수있는 일은 프로그래머 오류 일 가능성이있는 오류를 수정할 수 없습니다.


예,해야합니다. 당신은 변할 수 enum있지만 변할 수는 없습니다 switch. 미래에는 실수로 이어질 것입니다. 그게 throw new IllegalArgumentException(msg)좋은 습관 이라고 생각합니다 .


열거 형 상수가 너무 많고 소수의 경우에만 처리해야하는 경우 default나머지 상수는에서 처리합니다.

또한 열거 형 상수는 참조가 아직 설정되지 않은 경우 참조이거나 null. 이러한 경우도 처리해야 할 수 있습니다.


예, 누군가 enum에 값을 추가하기 전까지는 죽은 코드입니다. 그러면 switch 문이 'fail fast'( https://en.wikipedia.org/wiki/Fail-fast ) 원칙을 따릅니다.

이것은 다음 질문과 관련 될 수 있습니다. 컴파일 타임에 열거 형 스위치의 완전성을 보장하는 방법은 무엇입니까?


많은 사람들이 지적한 열거 형의 미래 확장 가능성 외에도 언젠가 누군가가 당신을 '개선' getColor()하거나 파생 클래스에서 재정의하고 잘못된 값을 반환하도록 할 수 있습니다. 물론 누군가가 명시 적으로 안전하지 않은 유형 캐스팅을 강요하지 않는 한 컴파일러는이를 포착해야합니다.

그러나 나쁜 일이 일어나기 때문에 예상치 못한 경로 elsedefault경로를 방치 하지 않는 것이 좋습니다 .


아무도 이것을 언급하지 않은 것에 놀랐습니다. int를 열거 형으로 캐스팅 할 수 있으며 값이 열거 형 값 중 하나가 아니기 때문에 던지지 않습니다. 즉, 컴파일러는 모든 열거 형 값이 스위치에 있음을 알 수 없습니다.

Even if you write your code correctly, this really does come up when serializing objects that contain enums. A future version might add to the enum and your code choke on reading it back, or somebody looking to create mayhem may hexedit a new value in. Either way, running off the switch rarely does the right thing. So, we throw in default unless we know better.


Here is how I would handle it, beside NULL value which would result in a null pointer exception which you can handle.

If Color color is not null, it has to be one of the singletons in enum Color, if you assign any reference to an object that is not one of the them this will cause a Runtime error.

So my solution is to account for values that are not supported.

Test Run

Test.java

public Test
{
  public static void main (String [] args)
  {
    try { test_1(null); }
    catch (NullPointerException e) { System.out.println ("NullPointerException"); }

    try { test_2(null); }
    catch (Exception e) { System.out.println(e.getMessage()); }

    try { test_1(Color.Green); }
    catch (Exception e) { System.out.println(e.getMessage()); }
  }

  public static String test_1 (Color color) throws Exception
  {
    String out = "";

    switch (color) // NullPointerException expected
    {
      case Color.Red:
        out = Red.getName();
        break;
      case Color.Blue:
        out = Red.getName();
        break;
      default:
        throw new UnsupportedArgumentException ("unsupported color: " + color.getName());
    }

    return out;
  }

.. or you can consider null as unsupported too

  public static String test_2 (Color color) throws Exception
  {
    if (color == null) throw new UnsupportedArgumentException ("unsupported color: NULL");
    return test_1(color);
  }
}

Color.java

enum Color
{
  Red("Red"), Blue("Blue"), Green("Green");
  private final String name;
  private Color(String n) { name = n; }
  public String getName() { return name; }
}

UnsupportedArgumentException.java

class UnsupportedArgumentException extends Exception
{
  private String message = null;

  public UnsupportedArgumentException() { super(); }

  public UnsupportedArgumentException (String message)
  {
    super(message);
    this.message = message;
  }

  public UnsupportedArgumentException (Throwable cause) { super(cause); }

  @Override public String toString() { return message; }

  @Override public String getMessage() { return message; }
}

In this case using Assertion in default is the best practice.

참고URL : https://stackoverflow.com/questions/33019562/what-is-the-usage-of-default-when-the-switch-is-for-an-enum

반응형