简体   繁体   中英

Generic mechanism to introduce default values for enums in Java

On our current project we are mapping some magic numbers from database (sigh, I know) to java enums like so:

public interface WithCode {
    Integer getCode();
}


public enum Role implements WithCode {
    OWNER(1),
    ADMIN(2),
    USER(3);

    @Getter Integer code;

    Role(Integer code) {
        this.code = code;
    }
}

We have handful of these, so we created an utility that finds appropriate enum by ID like so:

public interface EnumLookuper {
    static <T extends Enum<T> & WithCode> T ofCode(int code, Class<T> enumType) {
        return Arrays.stream(enumType.getEnumConstants())
                .filter(value -> Objects.equals(value.getCode(), code))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException(String.format("Unknown %s with code %d", enumType.getName(), code)));
    }
}

Our algorithm is perhaps a bit too aggressive - it fails on unknown values. Some other team members have added new magic numbers to DB and our project started to throw bunch of exceptions.

Now, I'd like to add UNKNOWN value to each enum (we have tens of those) and I was thinking of doing it a bit more generically, so we'd change the exception throwing line to something like:

.orElseGet(() -> defaultEnumValue())

Enforcing the defaults with an interface wouldn't work really, as we'd need to provide default value for each enum member. Any ideas of introducing elegant fallback/unknown values for each enum?

EDIT

Using a custom "unknown value interface" would force me to implement unknown value per enum member, for example:

interface UnknownValueProvider<T> {
  T unknown();
}

public enum Role implements WithCode, UnknownValueProvider<Role> {
    OWNER(1) {
    public Role unknown() {
        return ...;
    }
}

(Note: I am unfamiliar with portions of the code you have, namely most of the 2nd code block that gets the enum by ID. It seems overly complex/unintuitive, and it's different from all the code I've found that performs a similar function, see here . Is there a reason for it to be like that that I'm missing?)


I ran into a similar issue before, with some minor differences. In my case, I was using valueOf() to get the enum, but I wanted to make it case insensitive. So, inside my enum, I overloaded(?) valueOf(anyCaseParam) to return valueOf(AnyCaseParam.toUpperCase()) .

My Suggestion

You should add UNKNOWN(-1) to the list of enums (so that you have an enum to retrieve). I would then use something like this answer has for a way to get the enum by its ID value.

From that answer, I modified the last bit of code to fit what you're asking:

public T getByID(int num) {

    // Old code: 
       //return map.get(num);


    int unknownEnum= -1;

    if(map.get(num)!=null)
      return map.get(num);
    else
      return map.get(unknownEnum);
}

If you wanted, you could even have another method that throws an exception instead of returning the UNKNOWN value.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM