使用 java 泛型迭代枚举值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2205891/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 04:43:39  来源:igfitidea点击:

Iterate enum values using java generics

javagenericsenumsenumeration

提问by Tauren

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. Unfortunately, in my use case, this value is often null, as there is a public Filter()no-arg constructor as well. This means I can't do a selectedOption.getClass() without getting an NPE. This filter class manages a list of available options which of the options is selected. When nothing is selected, selectedOption is null.

如果我可以确定 selectedOption 具有非空值,这将非常有效。不幸的是,在我的用例中,该值通常为 null,因为还有一个公共 Filter()无参数构造函数。这意味着我不能在没有获得 NPE 的情况下执行 selectedOption.getClass() 。此过滤器类管理可用选项列表,其中选择了哪些选项。当未选择任何内容时, 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 参数的情况下做到这一点?

采纳答案by Thirler

This is a hard problem indeed. One of the things you need to do is tell java that you are using an enum. This is by stating that you extend the Enum class for your generics. However this class doesn't have the values() function. So you have to take the class for which you can get the values.

这确实是一个难题。您需要做的一件事是告诉 java 您正在使用枚举。这是通过声明您为泛型扩展 Enum 类。但是这个类没有 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());
        }
}

回答by Steve McLeod

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.

现在您可以将列表传递给任何需要列表的方法。

回答by NawaMan

If you are sure that selectedOptionof the constructor Filter(T selectedOption)is not null. You can use reflection. Like this.

如果您确定selectedOption构造函数Filter(T selectedOption)的不为空。您可以使用反射。像这样。

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.

希望这可以帮助。

回答by Bozho

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

回答by Miserable Variable

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

}

回答by Boris Pavlovi?

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

回答by Benjamin Fuentes

Here below an example of a wrapper class around an Enum. Is a little bit weird bu is what i need :

下面是一个围绕 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;
}

}

}

回答by quantum

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 为我们提供了一种相对干净和更简洁的方法来实现这一点,而无需使用values()Enum 类中的合成:

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.

可以简单地调整代码以检索不同的元素或以不同的方式收集。

回答by Stephane

To get the valueof 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;
  }

}

回答by Pradeep Sreeram

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