Java 제네릭을 사용하여 열거 형 값 반복


81

제네릭을 사용하는 동안 열거 형 값을 반복하는 방법을 찾으려고합니다. 이 작업을 수행하는 방법 또는 가능한지 확실하지 않습니다.

다음 코드는 내가 원하는 것을 보여줍니다. 코드 T.values ​​() 는 다음 코드에서 유효하지 않습니다.

public class Filter<T> {
    private List<T> availableOptions = new ArrayList<T>();
    private T selectedOption;

    public Filter(T selectedOption) {
        this.selectedOption = selectedOption;
        for (T option : T.values()) {  // INVALID CODE
            availableOptions.add(option);
        }
    }
}

필터 개체를 인스턴스화하는 방법은 다음과 같습니다.

Filter<TimePeriod> filter = new Filter<TimePeriod>(TimePeriod.ALL);

열거 형은 다음과 같이 정의됩니다.

public enum TimePeriod {
    ALL("All"), 
    FUTURE("Future"), 
    NEXT7DAYS("Next 7 Days"), 
    NEXT14DAYS("Next 14 Days"), 
    NEXT30DAYS("Next 30 Days"), 
    PAST("Past"),
    LAST7DAYS("Last 7 Days"), 
    LAST14DAYS("Last 14 Days"),
    LAST30DAYS("Last 30 Days"); 

    private final String name;

    private TimePeriod(String name) {
        this.name = name;
    }

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

열거 형의 값을 목록에 복사하는 것이 이치에 맞지 않을 수도 있지만 값 목록이 입력으로 필요하고 열거 형으로 작동하지 않는 라이브러리를 사용하고 있습니다.


2010 년 2 월 5 일 수정 :

제안 된 대부분의 답변은 매우 유사하며 다음과 같이 제안합니다.

class Filter<T extends Enum<T>> {
    private List<T> availableOptions = new ArrayList<T>();
    private T selectedOption;

    public Filter(T selectedOption) {
        Class<T> clazz = (Class<T>) selectedOption.getClass();
        for (T option : clazz.getEnumConstants()) {
            availableOptions.add(option);
        }
    }
}

selectedOption에 null이 아닌 값이 있는지 확인할 수 있다면 이것은 잘 작동합니다. 불행히도 내 사용 사례에서는 public Filter () no-arg 생성자 가 있기 때문에이 값은 종종 null 입니다. 이것은 NPE없이 selectedOption.getClass ()를 수행 할 수 없음을 의미합니다. 이 필터 클래스는 어떤 옵션이 선택되었는지 사용 가능한 옵션 목록을 관리합니다. 아무것도 선택하지 않으면 selectedOption은 null입니다.

이 문제를 해결하기 위해 생각할 수있는 유일한 방법은 생성자의 클래스를 실제로 전달하는 것입니다. 그래서 다음과 같이 :

class Filter<T extends Enum<T>> {
    private List<T> availableOptions = new ArrayList<T>();
    private T selectedOption;

    public Filter(Class<T> clazz) {
        this(clazz,null);
    }

    public Filter(Class<T> clazz, T selectedOption) {
        this.selectedOption = selectedOption;
        for (T option : clazz.getEnumConstants()) {
            availableOptions.add(option);
        }
    }
}

생성자에 추가 클래스 매개 변수가 필요하지 않고이를 수행하는 방법에 대한 아이디어가 있습니까?


1
getClassenum 정수 익명 서브 클래스 (코드 인 경우는 실패 ). getDeclaringClass대신 사용해야합니다.
Radiodef

답변:


125

이것은 참으로 어려운 문제입니다. 해야 할 일 중 하나는 열거 형을 사용하고 있음을 Java에 알리는 것입니다. 이것은 제네릭에 대한 Enum 클래스를 확장한다는 것을 나타냅니다. 그러나이 클래스에는 values ​​() 함수가 없습니다. 따라서 값을 얻을 수있는 수업을 들어야합니다.

다음 예는 문제 해결에 도움이됩니다.

public <T extends Enum<T>> void enumValues(Class<T> enumType) {
        for (T c : enumType.getEnumConstants()) {
             System.out.println(c.name());
        }
}

c.name ()이 열거 형 항목의 이름을 반환하지 않습니까? 그 가치를 반환하는 것은 어떻습니까? QUIZ ( "quizzie") 항목의 경우 QUIZ가 아닌 "quizzie"를 반환한다고 가정 해 보겠습니다.
Stephane 2015

이를 위해서는 값을 반환하는 열거 형에 다음과 같은 메서드가 필요합니다 getFriendlyName(). 그런 다음 열거 형에 인터페이스를 추가 한 다음 위의 제네릭을 조정하여 열거 형과 유형에 대한 인터페이스를 모두 요구해야합니다 T. 이 함수는 다음과 같은 것이된다 :public <E extends Enum<?> & EnumWithFriendlyName> void friendlyEnumValues(Class<T> enumType)
Thirler

17

또 다른 옵션은 EnumSet을 사용하는 것입니다.

class PrintEnumConsants {

    static <E extends Enum <E>> void foo(Class<E> elemType) {
        for (E e : java.util.EnumSet.allOf(elemType)) {
            System.out.println(e);
        }
    }

    enum Color{RED,YELLOW,BLUE};
    public static void main(String[] args) {
        foo(Color.class);
    } 

}

6

완전성을 위해 JDK8은 values()Enum 클래스 에서 합성을 사용할 필요없이이를 달성 할 수있는 비교적 깨끗하고 간결한 방법을 제공합니다 .

간단한 열거 형이 주어지면 :

private enum TestEnum {
    A,
    B,
    C
}

그리고 테스트 클라이언트 :

@Test
public void testAllValues() {
    System.out.println(collectAllEnumValues(TestEnum.class));
}

다음과 같이 인쇄됩니다 {A, B, C}.

public static <T extends Enum<T>> String collectAllEnumValues(Class<T> clazz) {
    return EnumSet.allOf(clazz).stream()
            .map(Enum::name)
            .collect(Collectors.joining(", " , "\"{", "}\""));
}

다른 요소를 검색하거나 다른 방식으로 수집하도록 코드를 사소하게 조정할 수 있습니다.


5

안전하지 않은 캐스트 사용 :

class Filter<T extends Enum<T>> {
    private List<T> availableOptions = new ArrayList<T>();
    private T selectedOption;

    public Filter(T selectedOption) {
        Class<T> clazz = (Class<T>) selectedOption.getClass();
        for (T option : clazz.getEnumConstants()) {
            availableOptions.add(option);
        }
    }
}

getClass열거 형 상수가 익명의 하위 클래스이면 실패합니다 ( example ). getDeclaringClass대신 사용해야합니다.
Radiodef

1

selectedOption생성자 Filter(T selectedOption)가 null이 아닌 것이 확실한 경우 . 반사를 사용할 수 있습니다. 이렇게.

public class Filter<T> {
    private List<T> availableOptions = new ArrayList<T>();
    private T selectedOption;

    public Filter(T selectedOption) {
        this.selectedOption = selectedOption;
        for (T option : this.selectedOption.getClass().getEnumConstants()) {  // INVALID CODE
            availableOptions.add(option);
        }
    }
}

도움이 되었기를 바랍니다.


1

일반 열거 의 을 가져 오려면 다음 을 수행하십시오.

  protected Set<String> enum2set(Class<? extends Enum<?>> e) {
    Enum<?>[] enums = e.getEnumConstants();
    String[] names = new String[enums.length];
    for (int i = 0; i < enums.length; i++) {
      names[i] = enums[i].toString();
    }
    return new HashSet<String>(Arrays.asList(names));
  }

위의 메서드에서 toString () 메서드에 대한 호출에 유의하십시오.

그런 다음 이러한 toString () 메서드를 사용하여 열거 형을 정의합니다.

public enum MyNameEnum {

  MR("John"), MRS("Anna");

  private String name;

  private MyNameEnum(String name) {
    this.name = name;
  }

  public String toString() {
    return this.name;
  }

}

0

근본 문제는 배열을 목록으로 변환해야한다는 것입니다. 그렇죠? 특정 유형 (T 대신 TimePeriod)과 다음 코드를 사용하여이를 수행 할 수 있습니다.

따라서 다음과 같이 사용하십시오.

List<TimePeriod> list = new ArrayList<TimePeriod>();
list.addAll(Arrays.asList(sizes));

이제 목록을 원하는 모든 메소드에 목록을 전달할 수 있습니다.


0

필터를 다음과 같이 선언하면

public class Filter<T extends Iterable>

그때

import java.util.Iterator;

public enum TimePeriod implements Iterable {
    ALL("All"),
    FUTURE("Future"),
    NEXT7DAYS("Next 7 Days"),
    NEXT14DAYS("Next 14 Days"),
    NEXT30DAYS("Next 30 Days"),
    PAST("Past"),
    LAST7DAYS("Last 7 Days"),
    LAST14DAYS("Last 14 Days"),
    LAST30DAYS("Last 30 Days");

    private final String name;

    private TimePeriod(String name) {
        this.name = name;
    }

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

    public Iterator<TimePeriod> iterator() {
        return new Iterator<TimePeriod>() {

            private int index;

            @Override
            public boolean hasNext() {
                return index < LAST30DAYS.ordinal();
            }

            @Override
            public TimePeriod next() {
                switch(index++) {
                    case    0   : return        ALL;
                    case    1   : return        FUTURE;
                    case    2   : return        NEXT7DAYS;
                    case    3   : return        NEXT14DAYS;
                    case    4   : return        NEXT30DAYS;
                    case    5   : return        PAST;
                    case    6   : return        LAST7DAYS;
                    case    7   : return        LAST14DAYS;
                    case    8   : return        LAST30DAYS;
                    default: throw new IllegalStateException();
                }
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }
}

그리고 사용법은 매우 쉽습니다.

public class Filter<T> {
    private List<T> availableOptions = new ArrayList<T>();
    private T selectedOption;

    public Filter(T selectedOption) {
        this.selectedOption = selectedOption;
        Iterator<TimePeriod> it = selectedOption.iterator();
        while(it.hasNext()) {
            availableOptions.add(it.next());
        }
    }
}

흥미로운 접근 방식. Class <T>를 선언 할 필요없이 이것이 작동 할 것이라고 생각합니다. 그러나 모든 열거 형을 변경할 필요가 없기 때문에 다른 방법을 사용하고 있습니다.
Tauren

0

다음은 Enum을 둘러싼 래퍼 클래스의 예입니다. 조금 이상한 부가 내가 필요한 것입니다.

public class W2UIEnum<T extends Enum<T> & Resumable> {

public String id;
public String caption;

public W2UIEnum(ApplicationContext appContext, T t) {
    this.id = t.getResume();
    this.caption = I18N.singleInstance.getI18nString(t.name(), "enum_"
            + t.getClass().getSimpleName().substring(0, 1).toLowerCase()
            + t.getClass().getSimpleName().substring(1,
                    t.getClass().getSimpleName().length()), appContext
            .getLocale());
}

public static <T extends Enum<T> & Resumable> List<W2UIEnum<T>> values(
        ApplicationContext appContext, Class<T> enumType) {
    List<W2UIEnum<T>> statusList = new ArrayList<W2UIEnum<T>>();
    for (T status : enumType.getEnumConstants()) {
        statusList.add(new W2UIEnum(appContext, status));
    }
    return statusList;
}

}


0

이렇게 했어요

    protected List<String> enumToList(Class<? extends Enum<?>> e) {
            Enum<?>[] enums = e.getEnumConstants();
            return Arrays.asList(enums).stream()
                   .map(name -> name.toString())
                   .collect(Collectors.toList());
        }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.