使用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对象的方法:

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

我意识到将枚举的值复制到列表中可能没有意义,但是我使用的库需要一个数值列表作为输入,并且不适用于枚举。


编辑2/5/2010:

提出的大多数答案都非常相似,并建议执行以下操作:

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值,那么这将非常有用。不幸的是,在我的用例中,该值通常为null,因为有一个公共Filter() no-arg构造函数。这意味着我不能在没有NPE的情况下执行selectedOption.getClass()。该过滤器类管理可用选项的列表,其中选择了哪个选项。如果未选择任何内容,则selectedOption为null。

我唯一想解决的办法是在构造函数中实际传递一个Class。所以像这样:

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

有什么想法如何在构造函数中不需要额外的Class参数的情况下执行此操作?


1
所述getClass如果枚举常数是匿名的子类(代码也将失败例子)。getDeclaringClass应该改为使用。
Radiodef

Answers:


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”)项目,返回“ quizzie”而不是QUIZ。
Stephane 2015年

为此,您需要在枚举上有一个返回值的方法,例如:getFriendlyName()。然后,您必须向枚举添加接口,然后修改上面的泛型以同时要求枚举和type的接口T。然后,该函数将变为:public <E extends Enum<?> & EnumWithFriendlyName> void friendlyEnumValues(Class<T> enumType)
Thirler

16

另一个选择是使用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如果枚举常量是匿名子类,则将失败(示例)。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

根本问题是您需要将数组转换为列表,对吗?您可以通过使用特定类型(TimePeriod而不是T)和以下代码来完成此操作。

因此,请使用以下内容:

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

现在,您可以将list传递到任何需要列表的方法中。


0

如果您将Filter声明为

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>就可以。但是,我希望不必更改所有枚举,因此我倾向于使用其他方法。
牛头人2010年

0

下面是围绕枚举的包装器类的示例。我需要的是一点奇怪的东西吗?

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());
        }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.