views:

3433

answers:

8

I have a handful of helper methods that convert enum values into a list of strings suitable for display by an HTML <select> element. I was wondering if it's possible to refactor these into a single polymorphic method.

This is an example of one of my existing methods:

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

I'm still pretty new to Java generics, so I'm not sure how to pass a generic enum to the method and have that used within the for loop as well.

Note that I know that the enums in question will all have that getDisplayValue method, but unfortunately they don't share a common type that defines it (and I can't introduce one), so I guess that will have to be accessed reflectively...?

Thanks in advance for any help.

A: 

(sorry, this is C#. I didn't see that the question was for Java.)

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

For Java, of course, all enum types still inherit from java.util.Enum, so you can write:

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

Since java.util.Enum doesn't actually implement values(), I think that reflection is the only way to go.

JSBangs
your Java code doesn't even compile; FYI use aClass.getEnumValues() in Java in place of Enum.GetNames(typeof(T))
dfa
A: 

There are two things you can do here. The first (simpler, and therefore better) way would just be to have your getStrings() method take a list of some interface, and make your enums implement that interface:

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

If you really care internally that the type is an Enum, you can also use the fact that all enumerated types automatically subclass the built-in type Enum. So, for your example (disclaimer: I think this compiles, but haven't actually tried it):

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

This second form can be useful if you want to do something that specifically requires an enumeration (e.g. use an EnumMap or EnumSet for some reason); otherwise, I'd go with the first (since with that method, you can also use non-enumerated types, or just a subset of the enumeration).

Sbodd
Thanks, but I already explained that the first option is not available to me.
John Topley
A: 

Note that I know that the enums in question will all have that getDisplayValue method, but unfortunately they don't share a common type that defines it (and I can't introduce one), so I guess that will have to be accessed reflectively...?

You are guessing correctly. An alternative would be to have the enums all implement toString() by returning the display value - but if you can't have them implement an interface then I suppose that's not possible either.

Michael Borgwardt
Unfortunately I can't do that either, because the output of toString has to match values in the database that I can't change!
John Topley
+5  A: 

You can stick this method in some utility class:

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

Source: Examining Enums

John Calsbeek
Doesn't use the getDisplayValue method.
John Topley
Yeah, realized that after I posted. It does now.
John Calsbeek
Where does Displayable come from?
John Topley
Whoops, can't add an interface; I'll edit this to do it via reflection.
John Calsbeek
Unfortunately that's not an option as explained to Sbodd and in the original question.
John Topley
+1  A: 

I'd use a java.util.ResourceBundle with a bundle file that maps to the toString (and maybe class name) values of your enums so your code then becomes something like:

bundle.getString(enum.getClass().getName() + enum.toString());
Nick Holt
+6  A: 

using Class#getEnumConstants() is simple:

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

this is not completely typesafe since you can have an Enum without a getDisplayValue method.

dfa
Doesn't use the getDisplayValue method.
John Topley
modified, please review exception handling
dfa
Works nicely, thanks!
John Topley
If it's a heavily used method and/or you have a lot of Enums, then you may want to consider having that array calculated once as a static during class initialization rather than recreating it every time.
Chris Kessel
+1  A: 

Here is how I would suggest going about it:

First a helper method and static inner class in a utility class somewhere:

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

And then put that together with your enum:

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

If performance is an issue aroung generating so many proxy objects, then I would go along the path of making a mutable class that implements DisplayableEnum that can change with each enum constant (kind of a flyweight pattern) and have an invocation handler there that is more flexible about its real object and invokes the right one on every pass through the loop.

Yishai
+2  A: 

This approach avoids reflection:

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

...but does require a separate type for everything you want to display:

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

In this case, an anonymous type is used, but it could be a stateless singleton or somesuch.

McDowell