HOWTO · Java

Java에서 열거형 확장

이 자습서는 Java에서 열거형 기능을 확장하는 방법을 보여줍니다.

이 튜토리얼은 Java에서 enum 기능을 확장하는 방법을 보여줍니다.

Java에서 enum 확장

enumbyte 코드에서 enum이 여러 정적 멤버가 있는 클래스로 표시되고 추상 java.lang.Enum에서 상속되기 때문에 enum을 일종의 컴파일러 마법으로 간주할 수 있습니다.

이것이 enum이 다른 클래스 또는 enum을 확장할 수 없는 이유입니다. Java에서 enum을 확장할 수 없기 때문에 어떤 클래스에서도 enum을 확장할 수 없습니다. 다음 예제 코드를 사용하여 배우고 무슨 일이 일어나는지 봅시다.

예제 코드:

package delftstack;

enum Cars { Audi, BMW, Marcedes }

public class Example extends Cars {
  public static void main(String... args) {}
}

위의 코드에는 Cars라는 enum이 있고 Example 클래스는 이를 확장하려고 합니다. 출력 참조:

/Example.java:5: error: cannot inherit from final Cars
public class Example extends Cars {
                             ^
/Example.java:5: error: enum types are not extensible
public class Example extends Cars {
       ^
2 errors

보시다시피 클래스는 enum을 확장할 수 없습니다. 따라서 enum을 확장하는 것이 불가능하더라도 그 기능을 계속 확장할 수 있습니까?

기능은 enum에서 구현된 메소드로 정의할 수 있습니다. 예를 들어 위 코드의 enum Cars는 각 멤버에 대한 추상 메서드를 선언할 수 있습니다. 예를 참조하십시오.

enum Cars {
  Audi {
    @Override
    public void drive() {}
  },
  BMW {
    @Override
    public void drive() {}
  },
  Mercedes {
    @Override
    public void drive() {}
  },
  ;
  public abstract void drive();
}

보시다시피 각 구성원은 drive() 메서드를 재정의할 수 있습니다. 그러나 불행히도 다음과 같은 이유로 enum에서 메소드를 생성하는 것이 항상 가능한 것은 아닙니다.

  • enum이 타사 라이브러리 또는 다른 팀에 속하는 경우 추상 메서드 구현을 허용하지 않습니다.
  • drive() 메소드에 필요한 종속성이 없는 모듈에 속하는 경우.
  • enum이 다른 기능 및 데이터로 오버로드되면 읽을 수 없습니다.

이러한 문제를 해결하고 Java에서 enum의 기능을 확장할 수 있는 몇 가지 솔루션이 제공됩니다.

해결 방법 1: Java에서 enum 미러링

이름에서 알 수 있듯이 동일한 데이터로 다른 enum을 만들어야 합니다. 그 새로운 enumdrive() 메서드도 구현하므로 이제 두 개의 열거형이 있습니다.

enum Cars에 대한 예제 코드:

enum Cars {
  Audi {
    @Override
    public void drive() {}
  },
  BMW {
    @Override
    public void drive() {}
  },
  Mercedes {
    @Override
    public void drive() {}
  },
  ;
  public abstract void drive();
}

enum DriveCars에 대한 예제 코드:

enum DriveCars {
  Audi {
    @Override
    public void drive() {}
  },
  BMW {
    @Override
    public void drive() {}
  },
  Mercedes {
    @Override
    public void drive() {}
  },
  ;
  public abstract void drive();
}

두 번째 enum은 첫 번째 항목의 미러입니다. 이제 기능을 확장하여 이 열거형을 모두 사용할 수 있습니다. name()valueof()인 내장 enum 메서드를 사용해야 합니다.

사용 방법에 대한 다음 예를 참조하십시오.

Cars cars = ... DriveCars.valueOf(cars.name()).drive();

위의 코드는 enum Cars 기능이 enum DriveCars에서 어떻게 사용되는지 보여줍니다. 이는 기능이 확장되었음을 의미합니다.

위 코드에서 name() 메서드는 재정의할 수 없는 final이며 valueOf 메서드는 컴파일러에서 생성됩니다. 이 두 가지 방법은 확장 작업에서 기능 오류가 없다는 점에서 서로 잘 맞습니다.

enum Cars가 변경되면 위의 코드에 한 가지 문제가 있습니다. enum DriveCars는 전혀 알지 못하고 namevalueof 트릭의 실패를 유발합니다. 이 문제를 해결하려면 상위 미러가 Cars임을 DriveCars에 알려야 합니다.

이를 위해 정적 초기화 프로그램을 사용하여 DriveCarsCars를 비교할 수 있습니다. 두 열거형이 일치하지 않으면 예외가 발생합니다. 다음은 enumus 라이브러리의 예입니다.

enum DriveCars {
  ....static { Mirror.of(Cars.class); }
}

유틸리티 클래스는 두 열거형이 일치하는지 여부를 확인합니다. 이 메소드는 name()valueOf() 트릭을 검증합니다.

해결 방법 2: Java에서 enum 매핑

하나의 메소드만 보유하는 다른 enum을 작성하지 않으려는 경우. 이 경우 enum 대신 interface를 사용할 수 있습니다. 아래 예를 참조하십시오.

public interface Driver {
  void drive();
}

이제 enum Cars와 함께 이 인터페이스 드라이브를 사용하기 위해 그들 사이에 매핑을 만들 수 있습니다. 지도의 예는 다음과 같습니다.

Map<Cars, Driver> drivers = new EnumMap<>(Cars.class) {
  {
    put(Audi, new Driver() {
      @Override
      public void driver() {}
    }) put(BMW, new Driver() {
      @Override
      public void driver() {}
    }) put(Mercedes, new Driver() {
      @Override
      public void driver() {}
    })
  }
}

이제 이를 사용하려면 다음과 같은 간단한 코드를 사용하십시오.

drivers.get(Cars).drive();

위의 코드에서 사용된 EnumMap은 각 enum 멤버가 한 번만 표시되도록 보장하지만 각 멤버에 대한 항목을 보장하지는 않습니다.

지도의 크기가 enum의 멤버 수와 같은지 확인할 수 있습니다.

drivers.size() == Cars.values().length

enumus 라이브러리는 이 경우에 대한 유틸리티도 제공합니다. 지도가 Cars에 맞지 않으면 IllegalStateException이 발생합니다. 다음은 유틸리티입니다.

EnumMapValidator.validateValues(Cars.class, map, "Cars map");

위의 두 방법 모두 기능을 확장하여 enum을 강력하게 만드는 방법을 보여줍니다. enum을 직접 확장하는 것은 불가능하지만 이러한 트릭을 사용하여 기능을 확장할 수 있습니다.