简体   繁体   English

使用 java 泛型迭代枚举值

[英]Iterate enum values using java generics

I'm trying to find a way to iterate through an enum's values while using generics.我试图找到一种在使用泛型时迭代枚举值的方法。 Not sure how to do this or if it is possible.不确定如何执行此操作或是否可能。

The following code illustrates what I want to do.下面的代码说明了我想要做什么。 Note that the code T.values() is not valid in the following code.请注意,代码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);
        }
    }
}

Here is how I would instantiate a Filter object:以下是我将实例化 Filter 对象的方法:

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

The enum is defined as follows:枚举定义如下:

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

I realize it might not make sense to copy a enum's values to a list, but I'm using a library that needs a list of values as input and won't work with enums.我意识到将枚举的值复制到列表可能没有意义,但我使用的库需要一个值列表作为输入,并且不能使用枚举。


EDIT 2/5/2010:编辑 2/5/2010:

Most of the answers proposed are very similar and suggest doing something like this:提出的大多数答案都非常相似,并建议做这样的事情:

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

This would work great if I can be sure that selectedOption has a non-null value.如果我可以确定 selectedOption 具有非空值,这将非常有效。 Unfortunately, in my use case, this value is often null, as there is a public Filter() no-arg constructor as well.不幸的是,在我的用例中,该值通常为 null,因为还有一个公共 Filter()无参数构造函数。 This means I can't do a selectedOption.getClass() without getting an NPE.这意味着我不能在没有获得 NPE 的情况下执行 selectedOption.getClass() 。 This filter class manages a list of available options which of the options is selected.此过滤器类管理可用选项列表,其中选择了哪些选项。 When nothing is selected, selectedOption is null.当未选择任何内容时, selectedOption 为空。

The only thing I can think to solve this is to actually pass in a Class in the constructor.我唯一能想到的解决这个问题就是在构造函数中实际传入一个类。 So something like this:所以像这样:

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

Any ideas how to do this without needing an extra Class parameter in the constructors?任何想法如何在构造函数中不需要额外的 Class 参数的情况下做到这一点?

This is a hard problem indeed.这确实是一个难题。 One of the things you need to do is tell java that you are using an enum.您需要做的一件事是告诉 java 您正在使用枚举。 This is by stating that you extend the Enum class for your generics.这是通过声明您为泛型扩展 Enum 类。 However this class doesn't have the values() function.但是这个类没有 values() 函数。 So you have to take the class for which you can get the values.所以你必须参加你可以获得价值的课程。

The following example should help you fix your problem:以下示例应该可以帮助您解决问题:

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

Another option is to use EnumSet:另一种选择是使用 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);
    } 

}

For completeness, JDK8 gives us a relatively clean and more concise way of achieving this without the need to use the synthethic values() in Enum class:为了完整起见,JDK8 为我们提供了一种相对干净和更简洁的方法来实现这一点,而无需使用 Enum 类中的合成values()

Given a simple enum:给定一个简单的枚举:

private enum TestEnum {
    A,
    B,
    C
}

And a test client:还有一个测试客户端:

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

This will print {A, B, C} :这将打印{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(", " , "\"{", "}\""));
}

Code can be trivially adapted to retrieve different elements or to collect in a different way.可以简单地调整代码以检索不同的元素或以不同的方式收集。

Using an unsafe cast:使用不安全的强制转换:

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

If you are sure that selectedOption of the constructor Filter(T selectedOption) is not null.如果你确信selectedOption构造函数的Filter(T selectedOption)不为空。 You can use reflection.您可以使用反射。 Like this.像这样。

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

Hope this helps.希望这可以帮助。

If you declare Filter as如果您将 Filter 声明为

public class Filter<T extends Iterable>

then然后

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

And usage is quite easy:用法很简单:

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

To get the value of the generic enumeration:要获取通用枚举的

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

Note in the above method the call to the toString() method.请注意上述方法中对 toString() 方法的调用。

And then define the enumeration with such a toString() method.然后用这样的 toString() 方法定义枚举。

public enum MyNameEnum {

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

  private String name;

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

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

}

The root problem is that you need to convert an array to a list, right?根本问题是您需要将数组转换为列表,对吗? You can do this, by using a specific type (TimePeriod instead of T), and the following code.您可以通过使用特定类型(TimePeriod 而不是 T)和以下代码来执行此操作。

So use something like this:所以使用这样的东西:

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

Now you can pass list into any method that wants a list.现在您可以将列表传递给任何需要列表的方法。

Here below an example of a wrapper class around an Enum.下面是一个围绕 Enum 的包装类示例。 Is a little bit weird bu is what i need :有点奇怪,但是我需要的是:

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

} }

I did it like this我是这样做的

    protected List<String> enumToList(Class<? extends Enum<?>> e) {
            Enum<?>[] enums = e.getEnumConstants();
            return Arrays.asList(enums).stream()
                   .map(name -> name.toString())
                   .collect(Collectors.toList());
        }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM