Lặp lại các giá trị enum bằng cách sử dụng java generics


81

Tôi đang cố gắng tìm cách lặp lại các giá trị của enum trong khi sử dụng generic. Không chắc chắn về cách thực hiện điều này hoặc nếu có thể.

Đoạn mã sau minh họa những gì tôi muốn làm. Lưu ý rằng mã T.values ​​() không hợp lệ trong đoạn mã sau.

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

Đây là cách tôi khởi tạo đối tượng Bộ lọc:

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

Enum được định nghĩa như sau:

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

Tôi nhận thấy có thể không hợp lý khi sao chép các giá trị của enum vào một danh sách, nhưng tôi đang sử dụng một thư viện cần danh sách các giá trị làm đầu vào và sẽ không hoạt động với enum.


CHỈNH SỬA 2/5/2010:

Hầu hết các câu trả lời được đề xuất đều rất giống nhau và bạn nên làm như sau:

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

Điều này sẽ hoạt động tốt nếu tôi có thể chắc chắn rằng selectOption có giá trị khác rỗng. Thật không may, trong trường hợp sử dụng của tôi, giá trị này thường là null, vì cũng có một phương thức khởi tạo Filter () no-arg công khai. Điều này có nghĩa là tôi không thể thực hiện một selectOption.getClass () mà không nhận được NPE. Lớp bộ lọc này quản lý một danh sách các tùy chọn có sẵn mà các tùy chọn được chọn. Khi không có gì được chọn, tùy chọn đã chọn là trống.

Điều duy nhất tôi có thể nghĩ để giải quyết vấn đề này là thực sự truyền vào một Class trong hàm tạo. Vì vậy, một cái gì đó như thế này:

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

Bất kỳ ý tưởng nào về cách thực hiện điều này mà không cần tham số Lớp bổ sung trong các hàm tạo?


1
Các getClassmã cũng sẽ thất bại nếu hằng số enum là một lớp con nặc danh ( ví dụ ). getDeclaringClassnên được sử dụng thay thế.
Radiodef

Câu trả lời:


125

Đây quả thực là một vấn đề khó. Một trong những điều bạn cần làm là nói với java rằng bạn đang sử dụng một enum. Điều này là bằng cách nói rằng bạn mở rộng lớp Enum cho generic của bạn. Tuy nhiên lớp này không có hàm giá trị (). Vì vậy, bạn phải học lớp mà bạn có thể nhận được các giá trị.

Ví dụ sau sẽ giúp bạn khắc phục sự cố của mình:

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

Không phải c.name () trả về tên của mục enum? Làm thế nào về việc trả lại giá trị của nó? Giả sử, đối với một mục QUIZ ("quizzie"), trả về "quizzie" chứ không phải QUIZ.
Stephane

Cho rằng bạn sẽ cần phải có một phương pháp trên enum mà trả về giá trị nói: getFriendlyName(). Sau đó, bạn phải thêm một giao diện vào enum của mình và sau đó điều chỉnh các generic ở trên để yêu cầu cả enum VÀ giao diện cho loại T. Sau đó, hàm sẽ trở thành một cái gì đó giống như:public <E extends Enum<?> & EnumWithFriendlyName> void friendlyEnumValues(Class<T> enumType)
Thirler

16

Một tùy chọn khác là sử dụng 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

Để hoàn thiện, JDK8 cung cấp cho chúng ta một cách tương đối rõ ràng và ngắn gọn hơn để đạt được điều này mà không cần phải sử dụng tổng hợp values()trong lớp Enum:

Đưa ra một enum đơn giản:

private enum TestEnum {
    A,
    B,
    C
}

Và một khách hàng thử nghiệm:

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

Điều này sẽ in {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(", " , "\"{", "}\""));
}

Mã có thể được điều chỉnh nhỏ để lấy các phần tử khác nhau hoặc để thu thập theo một cách khác.


5

Sử dụng dàn diễn viên không an toàn:

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

getClasssẽ không thành công nếu hằng số enum là một lớp con ẩn danh ( ví dụ ). getDeclaringClassnên được sử dụng thay thế.
Radiodef

1

Nếu bạn chắc chắn rằng selectedOptionhàm tạo Filter(T selectedOption)không rỗng. Bạn có thể sử dụng sự phản chiếu. Như thế này.

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

Hi vọng điêu nay co ich.


1

Để nhận giá trị của kiểu liệt kê chung:

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

Lưu ý trong phương thức trên gọi phương thức toString ().

Và sau đó xác định kiểu liệt kê với một phương thức toString () như vậy.

public enum MyNameEnum {

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

  private String name;

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

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

}

0

Vấn đề gốc rễ là bạn cần chuyển một mảng thành một danh sách, phải không? Bạn có thể làm điều này bằng cách sử dụng một loại cụ thể (TimePeriod thay vì T) và mã sau.

Vì vậy, sử dụng một cái gì đó như thế này:

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

Bây giờ bạn có thể chuyển danh sách vào bất kỳ phương thức nào muốn có danh sách.


0

Nếu bạn khai báo Bộ lọc là

public class Filter<T extends Iterable>

sau đó

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

Và cách sử dụng khá dễ dàng:

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

Cách tiếp cận thú vị. Tôi nghĩ rằng điều này sẽ hoạt động mà không cần phải khai báo Class <T>. Tuy nhiên, tôi không muốn phải thay đổi tất cả các enums của mình, vì vậy tôi đang nghiêng về một phương pháp khác.
Tauren

0

Dưới đây là ví dụ về một lớp wrapper xung quanh Enum. Có một chút kỳ lạ bu là những gì tôi cần:

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

Tôi đã làm nó như thế này

    protected List<String> enumToList(Class<? extends Enum<?>> e) {
            Enum<?>[] enums = e.getEnumConstants();
            return Arrays.asList(enums).stream()
                   .map(name -> name.toString())
                   .collect(Collectors.toList());
        }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.