10
\$\begingroup\$

I'm trying to do some util class to operate on enums - convert Enums to its special strings representation via interface method. String stored in enum constructors.

public enum StatusEnum implements HumanReadableEnum {
    NOT_VERIFIED("Not verified message"),
    INVALID("Invalid message"),
    APPROVED("Approved message");

    private String text;
    public String getHumanReadable(){   // single method of HumanReadableEnum
        return text;
    }

For example instead of HumanReadableEnum[] v = StatusEnum.values() I want to get List of inner text messages and do it in generic style.

So now I made a toHumanReadableCollection method:

public class EnumUtils {
    public static <CustomEnum extends Enum & HumanReadableEnum, E extends Class<CustomEnum>> 
    List<String> toHumanReadableCollection(E enumClass){
        Enum[] values = enumClass.getEnumConstants();
        List<String> humanReadableCollection = new ArrayList<>(values.length);

        for (Enum value : values) {
            String stringRepresentation = ((CustomEnum)value).getHumanReadable();
            humanReadableCollection.add(stringRepresentation);
        }
        return humanReadableCollection;
    }
}

As a result I can do something like:

List<String> messagesOfEnum1 = EnumUtils.toHumanReadableCollection(MyEnum1.class);
List<String> messagesOfEnum2 = EnumUtils.toHumanReadableCollection(MyEnum2.class);

And have code of toHumanReadableCollection in single place. But I have some doubts about performance. So could somebody help me to understand the cost of my code? :) Did I touched the reflection?)

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Have you by any chance come across the toString() method? \$\endgroup\$
    – h.j.k.
    Commented Oct 2, 2015 at 15:43

3 Answers 3

10
\$\begingroup\$

Method header

Your method header can be improved, the generic type E is not necessary, it can instead be written as:

public static <CustomEnum extends Enum & HumanReadableEnum> 
    List<String> toHumanReadableCollection(Class<CustomEnum> enumClass){

Type parameters should generally be one-character names, so this would be better as:

public static <E extends Enum & HumanReadableEnum> 
    List<String> toHumanReadableCollection(Class<E> enumClass) {

Compiler warning

You're getting a compiler warning here:

String stringRepresentation = ((CustomEnum)value).getHumanReadable();

All you care about is the getHumanReadable method, which is in the HumanReadableEnum interface, so make that:

String stringRepresentation = ((HumanReadableEnum)value).getHumanReadable();

Naming and interface

HumanReadableEnum should instead be HumanReadable, there's no need to keep Enum in the name, there's no need to restrict the interface to only enums either.

Your questions

Reflection? Yes, this line of code is using reflection:

Enum[] values = enumClass.getEnumConstants();

Time complexity? \$O(n)\$, it is linear to the number of enum constants.

Java 8

In Java 8, you could replace your method with this:

List<String> list = Arrays.stream(StatusEnum.values())
   .map(HumanReadableEnum::getHumanReadable)
   .collect(Collectors.toList());

Or, as a general method, and also incorporating the earlier suggestions:

public static <E extends Enum & HumanReadable>
  List<String> toHumanReadableCollection(Class<E> enumClass) {
    Enum[] values = enumClass.getEnumConstants();
    return Arrays.stream(values)
       .map(e -> ((HumanReadable)e).getHumanReadable())
       .collect(Collectors.toList());
}

There's often a benefit of using Streams, I suggest you try to learn the Java 8 Stream API at some point.

\$\endgroup\$
1
  • \$\begingroup\$ an alternative would be: map(HumanReadable::cast).map(HumanReasable::getHumanReadable) - not sure if it s better or not though... \$\endgroup\$
    – assylias
    Commented Oct 2, 2015 at 15:42
5
\$\begingroup\$

Have you by any chance come across the toString() method? - myself

Just to elaborate a bit more on my comment... Is your solution perhaps looking for a non-existent problem in the first place? From Enum.toString()'s Javadoc:

Returns the name of this enum constant, as contained in the declaration. This method may be overridden, though it typically isn't necessary or desirable. An enum type should override this method when a more "programmer-friendly" string form exists.

And the best thing of all, toString() comes free with every Object:

Returns a string representation of the object. In general, the toString method returns a string that "textually represents" this object. The result should be a concise but informative representation that is easy for a person to read. It is recommended that all subclasses override this method.

Somewhat coincidentally, I have an answer on Programmers.SE to a similar question that may be of interest too.

The toString() method can be used as such:

enum StatusEnum {
    NOT_VERIFIED("Not verified message"),
    INVALID("Invalid message"),
    APPROVED("Approved message");

    private final String toString;

    private StatusEnum(String toString) {
        this.toString = toString;
    }

    @Override
    public String toString() {
        return toString;
    }
}

And the Java 8 approach for this may be:

private static <E extends Enum<E>> List<String> toStringForAll(Class<E> clazz) {
    return EnumSet.allOf(clazz).stream().map(Enum::toString).collect(Collectors.toList());
}
\$\endgroup\$
3
  • \$\begingroup\$ I think that when using an EnumSet, the order of the enums is not guaranteed. I like the suggestion of toString though, couldn't you even make that .map(Object::toString) ? \$\endgroup\$ Commented Oct 2, 2015 at 17:46
  • 1
    \$\begingroup\$ @SimonForsberg EnumSet's Javadoc says that "the iterator returned by the iterator method traverses the elements in their natural order", so the ordering is guaranteed. Object::toString works too, but Enum::toString saves 2 characters. :D \$\endgroup\$
    – h.j.k.
    Commented Oct 2, 2015 at 17:55
  • 1
    \$\begingroup\$ Oh, I have learned something new about EnumSet then! \$\endgroup\$ Commented Oct 2, 2015 at 18:01
3
\$\begingroup\$

If you look at the source for EnumMap (which needs to derive enum values from enum class object as well), you can see that it uses: SharedSecrets.getJavaLangAccess().getEnumConstantsShared(keyType);

Where SharedSecrets is documented as:

A repository of "shared secrets", which are a mechanism for calling implementation-private methods in another package without using reflection.

So if you really cared about performance, you might consider using this method. Be aware, though, that you will be served the original enum array this way and could do some pretty nasty stuff by mutating it. (Cf. this question on stackoverflow.)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.