6
\$\begingroup\$

Consider I have many enums (Java) that looks somewhat like this:

@AllArgsConstructor
@Getter
public enum PopularPeriod {
  MONTHLY  ("MON"),
  BIMONTHLY ("BMN"),
  QUARTERLY  ("QTR"),
  SEMI_ANNUALLY  ("SMA"),
  ANNUALLY  ("ANL");

  private final String value;
}

Now, a usual use, I'd think, would be to get the enum object using its value (the string) - there's an input you're parsing and you want to turn the string into some nice objects that make the code more readable.

So I added this to the enum above -

public static PopularPeriod fromString(String value) {
  for (PopularPeriod pp : PopularPeriod.values()) {
    if (pp.getValue().equalsIgnoreCase(value)) return pp;
  }

  return null;
}

(don't mind the returning of null the casing). This works quite good. But if you have dozens of those enums - you need to write it for each one.

I tried approaches like adding something like this to a utility class -

public static <T extends Enum<T>> T safeEnumByValue(Class<T> enumType, Object value) { ... }

but there I don't know of the private field (value).

So I though of "implementing" an interface instead (as enum may not extend base classes), and make the search as a default method in the interface.

public interface IEnum<T> {
  T getValue();
}

@AllArgsConstructor
@Getter
public enum PopularPeriod implements IEnum<String> { ... }

and adding a default method in the interface - but that's impossible as I don't have the PopularPeriod.values() there to work with.

In the end, I combined the two approaches. I have this method in a utility class:

public static <T extends IEnum> T safeEnumByValue(Class<T> enumType, Object value) {
  for (T enumConstant : enumType.getEnumConstants()) {
    if (enumConstant.getValue().equals(value)) return enumConstant;
  }

  return null;
}

Just got there.
It works.
I thought maybe I'd delete this question,but I would love to hear if there's any elegant way to do it.

\$\endgroup\$
  • \$\begingroup\$ Can you tell why you don't like the approach so we can tell you specifically why it can't be fixed. In other words, this is as elegant as you're going to get. \$\endgroup\$ – TorbenPutkonen Dec 3 '19 at 11:15
6
\$\begingroup\$

There may be a more fundamental problem here beyond converting a string to enum.

By adding a fromString(String) into the enum and defining the format specific strings in the enum constructor, you are coupling the enum tightly to the data format and making it an integral part of the input parser. Your enum now has two responsibilities, being clearly in violation of the single responsibility principle

Instead you should implement a format specific converter that converts the string used in transport to the enum used in computing and make the enum as bare as possible.

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

Agreed with the above if we implement the above function fromString(String value) we would definitely violate the SRP. However, we can implement a simple class with reverse mapping i.e. which would provide an easy way to convert between String and Enum. After that comparison is a cakewalk.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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