1

My code contain multiple enum like below. Basically that help to use enum via integer instead of enum value. Is it possible apply some sort of optimization like inheritance or something so that all can have behavior like below.

public enum DeliveryMethods {

    STANDARD_DOMESTIC(1), STANDARD_INTERNATIONAL(2), EXPRESS_DOMESTIC(3), EXPRESS_INTERNATIONAL(4);

    private final int code;

    private DeliveryMethods(int code) {
        this.code = code;
    }

    public int getCode() {
        return code;
    }

    private static final HashMap<Integer, DeliveryMethods> valueMap = new HashMap<>(2);

    static {
        for (DeliveryMethods type : DeliveryMethods.values()) {
            valueMap.put(type.code, type);
        }
    }

    public static DeliveryMethods getValue(int code) {
        return valueMap.get(code);
    }
}
5
  • 1
    Why would you want to use it via integer? That defeats some of the advantages of using an enum in the first place. Commented Jul 4, 2015 at 6:16
  • There are a number of reasons that might be needed, such as decoding of values from a binary file or DB. Commented Jul 4, 2015 at 6:18
  • What "optimization" are you after here? What isn't satisfactory about your current approach? Commented Jul 4, 2015 at 6:20
  • @KevinKrumwiede Unlike MySQL, MongoDB store String value of enum instead of Integer value. Commented Jul 4, 2015 at 6:24
  • @ChrisHayes This code will become repetitive if I use this in multiple enums. So I was looking for some function that I can call or some inheritance by which I can save this mechanism in generic enum that I can extend later. Commented Jul 4, 2015 at 6:26

3 Answers 3

5

Here is an example showing how you could delegate to another class:

public interface Keyed<K> {
    /**
     * returns the key of the enum
     */
    K getKey();
}

public class KeyEnumMapping<K, E extends Enum<?> & Keyed<K>> {
    private Map<K, E> map = new HashMap<>();

    public KeyEnumMapping(Class<E> clazz) {
        E[] enumConstants = clazz.getEnumConstants();
        for (E e : enumConstants) {
            map.put(e.getKey(), e);
        }
    }

    public E get(K key) {
        return map.get(key);
    }
}

public enum Example implements Keyed<Integer> {
    A(1),
    B(3),
    C(7);

    private static final KeyEnumMapping<Integer, Example> MAPPING = new KeyEnumMapping<>(Example.class);
    private Integer value;

    Example(Integer value) {
        this.value = value;
    }

    @Override
    public Integer getKey() {
        return value;
    }

    public static Example getByValue(Integer value) {
        return MAPPING.get(value);
    }

    public static void main(String[] args) {
        System.out.println(Example.getByValue(3));
    }
}

You could also avoid implementing a Keyed interface and simply pass a Function<E, K> to KeyEnumMapping constructor, that would transform the enum into its key:

public class KeyEnumMapping<K, E extends Enum<?>> {
    private Map<K, E> map = new HashMap<>();

    public KeyEnumMapping(Class<E> clazz, Function<E, K> keyExtractor) {
        E[] enumConstants = clazz.getEnumConstants();
        for (E e : enumConstants) {
            map.put(keyExtractor.apply(e), e);
        }
    }

    public E get(K key) {
        return map.get(key);
    }
}

public enum Example {
    A(1),
    B(3),
    C(7);

    private static final KeyEnumMapping<Integer, Example> MAPPING =
        new KeyEnumMapping<>(Example.class, Example::getValue);
    private Integer value;

    Example(Integer value) {
        this.value = value;
    }

    public Integer getValue() {
        return value;
    }

    public static Example getByValue(Integer value) {
        return MAPPING.get(value);
    }

    public static void main(String[] args) {
        System.out.println(Example.getByValue(3));
    }
}
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you sir. Perfect solution. This also help me to learn about Java 8 Function interface and double colon operator.
@DSingh The :: is not really an operator; Example::getValue is a method reference.
@Jesper I actually googled "double colon in java" and results stated it as operator. Thank you for pointing out this. I guess most of people who don't read Java 8 documentation will refer it as operator. But I updated myself. Thank you.
0

You can consider using the getOrdinal() method of Enum instead of maintaining the 'code' yourself.

http://docs.oracle.com/javase/7/docs/api/java/lang/Enum.html#ordinal()

Even if you maintain the 'code' attribute it is not necessary to maintain the 'valueMap'. Instead you can use the 'values()' method of Enum and iterate over all the enums.

5 Comments

This will break once you'll reorder enum values or add some value in the middle.
getOrdinal() means default integer value that start from 0 and that is independent of enum value. getOrdinal() will fail with DB values if in future position of enum value changes.
@kpavlov yes! exactly
Yes, as i said you can consider using the ordinal if it fits your requirement, even if code is maintained no need of maintaining 'valueMap'
@DevBlanked No ordinal are basic stuff, that mostly fails with DB. Different developers can change enum position.
0

There is no need for Hashmap unless until it is necessary.It's better to go with switch-case for enum values

I've written to get enum from Integer as well as string

  public enum DeliveryMethods {

    STANDARD_DOMESTIC(1), STANDARD_INTERNATIONAL(2), EXPRESS_DOMESTIC(3), EXPRESS_INTERNATIONAL(4);

    private final int code;

    private DeliveryMethods(int code) {
        this.code = code;
    }

    public int getCode() {
        return code;
    }

    public static DeliveryMethods fromString(String code) {
        if (code.matches("[1-4]")) {
            return fromInteger(Integer.valueOf(code));
        }
        throw new RuntimeException("No values for code " + code);
    }

    public static DeliveryMethods fromInteger(int code) {
        switch (code) {
            case 1:
                return STANDARD_DOMESTIC;
            case 2:
                return STANDARD_INTERNATIONAL;
            case 3:
                return EXPRESS_DOMESTIC;
            case 4:
                return EXPRESS_INTERNATIONAL;
        }
        throw new RuntimeException("No values for code " + code);
    }

  public static DeliveryMethods fromIntegerType2(int code) {
        for (DeliveryMethods d : DeliveryMethods.values()) {
            if (d.getCode() == code) {
                return d;
            }
        }
        throw new RuntimeException("No values for code " + code);
    }
}

4 Comments

one drawback of this method, when new enum values are introduced the code needs to be modified. So it's not that extensible
Yes you are adding a new enum means you've to add a new case as well..But this is an optimized approach as you've asked
@DevBlanked i've added a new approach check that.I guess that will suffice you
Thank you for your optimization. I guess this will be faster once compiled than my code. But my actual question is - Is it possible to combine this fromInteger() and fromString() for multiple enums ? So that I can call some function instead of repeating this code for every enum.

Your Answer

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