2

I have the following problem. I have a package with different enumeration classes in java.

What is the correct approach to avoid repeting myself when adding new methods that are common to all enums?

My problem:

channel.java

    public enum Channel {
           channel1, channel2;
    }

color.java

    public enum color{
           color1, color2;
    }

The method which should be used in both:

    public static boolean contains(String channel) {
       for (Channel c : Channel.values()) {
          if (c.name().equals(channel)) {
              return true;
          }
      }
    return false;
    }

Note that the loop has a reference to the enum itself. So, this will require copy and paste the method in all enums that I want to use it. Any suggestion?

Thanks Victor

4
  • The first question is why you need such a method in the first place. But apart from that, in Java 8 you can implement this using an interface and a default method. Whether it's a good idea to do so is another question. Commented May 20, 2015 at 9:21
  • Why does your Color enum need this method if it has nothing to do with Color? Commented May 20, 2015 at 9:26
  • @biziclop I was aware of that Java8 part but I'm limited to Java 6 to this particular case. Commented May 20, 2015 at 9:29
  • @Oli This is just a example name. I have several different enum types. The idea is that I have a set of parameters and they are usually compared a lot in the execution. Also, some of the won't change - that's why I choosed enums. Do you have a better suggestion for that case? Commented May 20, 2015 at 9:30

4 Answers 4

4

You can't do it quite the way you want to unfortunately. What you can do is to have some sort of util class that has a general mehod on it.

For example...

public class EnumUtils {

    public static boolean contains(Enum[] enumValues, String nameToCheck) {

        for(Enum each : enumValues) {
            if(each.name().equals(nameToCheck)) {
                return true;
            }
        }
        return false;
    }
}

Then you could use it like this...

System.out.println(EnumUtils.contains(Channel.values(), "channel1")); // TRUE
System.out.println(EnumUtils.contains(Color.values(), "octarine")); // FALSE

Caveat - In more complex systems, these sorts of static util classes are sometimes a bit of a "code-smell" but I think in your case it's fine.

For Java6:

     change each.name() => each.toString()
Sign up to request clarification or add additional context in comments.

3 Comments

I think that will do the job and I would like to vote this up, but I cant. Thanks for the caveat! I am fairly new to all this.
A more type-safe variation is to pass the class object for the enum and then use Class.getEnumConstants() to enumerate the constants within the method.
@biziclop Re: Using the class object... Nice! I didn't think of that.
2

I suggest to use an util-method like @Phil Anderson mentioned. I would only change it to a general pattern:

public static <T extends Enum<T>> void some_method(Class<T> clazz, String name) {
    try {
        T foundEnum = Enum.valueOf(clazz, name);
        // name matches to one of enum values, do something with it
    } catch (IllegalArgumentException e) {
        // name doesn't matches to any of enum values
    }
}

which in case of contains semantic could look like this:

public static <T extends Enum<T>> boolean contains(Class<T> clazz, String name) {
    try {
        Enum.valueOf(clazz, name);
    } catch (IllegalArgumentException e) {
        return false;
    }
    return true;
}

Updated:

As @phil-anderson mentioned, from performance point of view this method have certain disadvantages, because generation and throwing of exception is pretty slow (see How slow are Java exceptions?). But this is only a case, if method is invoked with an incorrect name value.

So, in this case you could use this pattern:

public static <T extends Enum<T>> void some_method(Class<T> clazz, String name) {
    for (T each : clazz.getEnumConstants()) {
        if (each.name().equals(name)) {
            // name matches to one of enum values, do something with it
        }
    }
    // name doesn't matches to any of enum values
}

Moreover, if performance plays an important role, especially if enum consists of large number of values, it is not efficient to iterate over (maybe) all of them. The solution could be using a lazy hash map for enums and get the value by a hashcode. For example:

@SuppressWarnings("unchecked")
public static <T extends Enum<T>> void some_method(Class<T> clazz, String name) {
    Map<String, Enum<?>> enumMap = enumsMap.get(clazz);
    if (enumMap == null) {
        enumMap = new HashMap<String, Enum<?>>();
        for (T t : clazz.getEnumConstants()) {
            enumMap.put(t.name(), t);
        }
        enumsMap.put(clazz, enumMap);
    }
    T t = (T) enumMap.get(name);
    if (t != null) {
        // name matches to one of enum values, do something with it
    } else {
        // name doesn't matches to any of enum values
    }
}

4 Comments

For some reason this is not giving the expected results. I'm not famiiliar with this structure so may be my bad. But If I try, for example: contains(Channel.class, "channel1"); I get that nothing is detected.
If you place contains-method in EnumUtil class, then System.out.println(EnumUtil.contains(Channel.class, "channel1")); returns true as expected.
Was a case-sensitive problem. Cheers.
I considered the valueof approach, but it's using exceptions for flow control which is not recommended (see Effective Java). That said, it was a pretty marginal call.
1

You can use an interface with a static method to do this:

public interface CanContainChannel {
    static boolean contains(String channel) {
        for (Channel c : Channel.values()) {
           if (c.name().equals(channel)) {
               return true;
           }
        }
        return false;
    }
}

And you both your enums can implement this interface to gain this method, although I'm not sure why you would want a method like this on your Color enum.

EDIT:

On clarification of the question I this question will help you: Iterate enum values using java generics

7 Comments

I think the idea is that each enum has its own version of the method, referring to itself. Plus there's not much point implementing an interface when all it has is a static method that doesn't even refer to the interface.
@biziclop I did think using default methods would be a possibility, but I'm unsure why a Color enum would want an instance method such as this. Although, you could make the case it doesn't need a static method like it either.
@biziclop And in his example the method he wanted on both was static
I think what OP wants is almost what Enum.valueOf() does.
@Oli Thanks for the help. But the point is, in this case, the enum in case is Channel. But, if in the enum Color, the loop should go around (Color c: Color.values()). Maybe I'm just trying a complex solution for a simple problem.
|
0

With reflection one can fetch the enum constants.

enum E {
    i("unu"), ii("du"), iii("tri"), iv("kvar");

    public final String name;

    E(String name) {
        this.name = name;
    }
}

public static void main(String[] args) {
    // Normally in Java 8 for a concrete enum:
    final String sought = "tri";
    boolean found = Stream.of(E.values()).anyMatch((c) -> c.name.equals(sought));

    // A generic function:
    System.out.println("okay:  "
        + find(E.class, (c) -> c.name.equals(sought)));
    System.out.println("fails: "
        + find(E.class, (c) -> c.name.equals("prtl")));
}

public static <T extends Enum<?>> boolean find(Class<T> clazz,
        Predicate<T> predicate) {
    return Stream.of(clazz.getEnumConstants()).anyMatch(predicate);
}

As you want to access a field, it is easier to pass the entire predicate.

Under java 7 one would do it generally more tight (without Stream) and need an interface for getName() etcetera.

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.