代码之家  ›  专栏  ›  技术社区  ›  John Topley

以多态方式将Java枚举值转换为字符串列表

  •  17
  • John Topley  · 技术社区  · 15 年前

    我有一些助手方法,可以将枚举值转换为适合HTML显示的字符串列表 <select> 元素。我想知道是否有可能将它们重构为一个多态方法。

    这是我现有方法之一的示例:

    /**
     * Gets the list of available colours.
     * 
     * @return the list of available colours
     */
    public static List<String> getColours() {
      List<String> colours = new ArrayList<String>();
    
      for (Colour colour : Colour.values()) {
        colours.add(colour.getDisplayValue());  
      }
    
      return colours;
    }
    

    我对Java泛型还是相当陌生,所以我不确定如何将泛型枚举传递给该方法,并在for循环中使用它。

    请注意,我知道所讨论的枚举都将具有该属性 getDisplayValue 方法,但不幸的是,它们不共享一个定义它的公共类型(我不能介绍一个),所以我猜必须以反射方式访问它。。。?

    10 回复  |  直到 15 年前
        1
  •  17
  •   dfa    15 年前

    使用 Class#getEnumConstants() 这很简单:

    static <T extends Enum<T>> List<String> toStringList(Class<T> clz) {
         try {
            List<String> res = new LinkedList<String>();
            Method getDisplayValue = clz.getMethod("getDisplayValue");
    
            for (Object e : clz.getEnumConstants()) {
                res.add((String) getDisplayValue.invoke(e));
    
            }
    
            return res;
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }
    

    这不是完全类型安全的,因为您可以在没有 getDisplayValue 方法。

        2
  •  10
  •   John Calsbeek    15 年前

    您可以将此方法粘贴到某些实用程序类中:

    public static <T extends Enum<T>> List<String> getDisplayValues(Class<T> enumClass) {
        try {
            T[] items = enumClass.getEnumConstants();
            Method accessor = enumClass.getMethod("getDisplayValue");
    
            ArrayList<String> names = new ArrayList<String>(items.length);
            for (T item : items)
                names.add(accessor.invoke(item).toString()));
    
            return names;
        } catch (NoSuchMethodException ex) {
            // Didn't actually implement getDisplayValue().
        } catch (InvocationTargetException ex) {
            // getDisplayValue() threw an exception.
        }
    }
    

    资料来源: Examining Enums

        3
  •  3
  •   Sbodd    15 年前

    在这里你可以做两件事。第一种(更简单,因此更好)方法是让getStrings()方法获取某个接口的列表,并让枚举实现该接口:

    public interface DisplayableSelection {
      public String getDisplayValue();
    }
    
    private static List<String> getStrings (Collection<DisplayableSelection> things) {
      List<String> retval = new ArrayList<String>();
      for (DisplayableSelection thing : things) {
        retval.add(thing.getDisplayValue());
      }
    }
    
    private static List<String> getColours () {
      return getStrings(Colour.values());
    }
    

    如果您在内部真正关心类型是一个枚举,那么您还可以使用这样一个事实,即所有枚举类型都会自动为内置类型枚举子类化。因此,作为您的示例(免责声明:I 认为 这可以编译,但实际上尚未尝试):

    public interface DisplayableEnum {
      public String getDisplayValue();
    }
    
    private static <T extends Enum<T> & DisplayableEnum > List<String> getDisplayValues(Class<T> pClass) {
      List<String> retval = new ArrayList<String>();
      for (DisplayableSelection thing : pClass.getEnumConstants()) {
        retval.add(thing.getDisplayValue());
      }
    }
    
    private static List<String> getColours () {
      return getStrings(Colour.class);
    }
    

    如果您想做一些特别需要枚举的事情(例如,出于某种原因使用枚举映射或枚举集),则第二种形式非常有用;否则,我将使用第一种方法(因为使用该方法,您还可以使用非枚举类型,或者仅使用枚举的子集)。

        4
  •  3
  •   McDowell rahul gupta    15 年前

    这种方法避免了反思:

      public static interface Displayer<T> {
        String displayName(T t);
      }
    
      public static <T> List<String> getDisplayNames(Iterable<? extends T> stuff,
          Displayer<T> displayer) {
        List<String> list = new ArrayList<String>();
        for (T t : stuff) {
          list.add(displayer.displayName(t));
        }
        return list;
      }
    

      enum Foo {
        BAR("BAR"), BAZ("BAZ");
        private final String displayName;
    
        private Foo(String displayName) {
          this.displayName = displayName;
        }
    
        public String getDisplayName() {
          return displayName;
        }
      }
    
      public static void main(String[] args) {
        Displayer<Foo> fooDisplayer = new Displayer<Foo>() {
          public String displayName(Foo foo) {
            return foo.getDisplayName();
          }
        };
    
        System.out.println(getDisplayNames(Arrays.asList(Foo.BAR, Foo.BAZ),
            fooDisplayer));
      }
    

    在本例中,使用了匿名类型,但它可以是无状态单例或类似的类型。

        5
  •  2
  •   Nick Holt    15 年前

    我会使用 java.util.ResourceBundle toString (可能还有类名)枚举的值,这样代码就会变成:

    bundle.getString(enum.getClass().getName() + enum.toString());
    
        6
  •  1
  •   Yishai    15 年前

    以下是我的建议:

    首先是某个实用程序类中的帮助器方法和静态内部类:

        @SuppressWarnings("unchecked")
        public static <T> T generateProxy(Object realObject, Class<?>... interfaces) {
            return (T) Proxy.newProxyInstance(realObject.getClass().getClassLoader(), interfaces, new SimpleInvocationHandler(realObject));
        }
    
    
        private static class SimpleInvocationHandler implements InvocationHandler {
            private Object invokee;
    
            public SimpleInvocationHandler(Object invokee) {
                this.invokee = invokee;
            }
    
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                method = invokee.getClass().getMethod(method.getName(), method.getParameterTypes());
                if (!method.isAccessible()) {
                    method.setAccessible(true);
                }
                try {
                    return method.invoke(invokee, args);
                } catch (InvocationTargetException e) {
                    throw e.getTargetException();
                }
            }
        }
    

    然后将其与枚举一起:

       interface DisplayableEnum {
           String getDisplayValue();
       }
    
    private static List<String> getFromEnum(Class<? extends Enum<?>> displayableEnum) {
        List<String> ret = new ArrayList<String>();
        for (Enum e : displayableEnum.getEnumConstants()) {
            DisplayableEnum de = generateProxy(e, DisplayableEnum.class);
            ret.add(de.getDisplayValue());
        }
        return ret;
    }
    

        7
  •  0
  •   Michael Borgwardt    15 年前

    请注意,我知道 问题是所有人都会有这样的想法 定义它的公共类型(以及 深思熟虑地。。。?

    toString() 通过返回显示值-但是如果您不能让他们实现接口,那么我认为这也不可能。

        8
  •  0
  •   Luigi Antonini    14 年前

    我在第一次响应时以这种方式编辑了方法,它工作起来没有问题,也没有实现任何接口

    public static <T extends Enum<T>> List<String> getDisplayValues(
            Class<T> enumClass) {
        try {
            T[] items = enumClass.getEnumConstants();
            Method accessor = enumClass.getMethod("toString");
    
            ArrayList<String> names = new ArrayList<String>(items.length);
            for (T item : items)
                names.add(accessor.invoke(item).toString());
    
            return names;
        } catch (NoSuchMethodException ex) {
            // Didn't actually implement getDisplayValue().
            Log.e(TAG, "getDisplayValues [" + ex+"]");
        } catch (InvocationTargetException ex) {
            // getDisplayValue() threw an exception.
            Log.e(TAG, "getDisplayValues [" + ex+"]");
        } catch (IllegalAccessException ex) {
            // getDisplayValue() threw an exception.
            Log.e(TAG, "getDisplayValues [" + ex+"]");
        }
        return null;
    }
    
        9
  •  -1
  •   JSBÕ±Õ¸Õ£Õ¹    15 年前

    (对不起,这是C#。我没有看到这个问题是针对Java的。)

    public string[] GetValues<T>()
    {
        return Enum.GetNames(typeof(T));
    }
    

    当然,对于Java,所有枚举类型仍然继承自Java.util.enum,因此您可以编写:

    public string[] getValues<T extends Enum<T>>()
    {
        // use reflection on T.class
    }
    

    因为java.util.Enum实际上并没有实现values(),所以我认为反射是唯一的方法。

        10
  •  -4
  •   Latency    13 年前

    来吧,伙计们。。没那么难。我正在使用字符串比较。。但是,如果需要,可以只比较对象类型。

    public static <T extends Enum<T>> Map<T, String>  Initialize_Map(Class<T> enumClass) {
      Map<T, String> map = new HashMap<T, String>();
      for (T val : enumClass.getEnumConstants()) {
        map.put(val, val.toString() + (val.toString().equals("ENUM_ELMT") ? " (appended)" : ""));
      }
      return map;       
    }