1

I'm using a Marker Interface to implement pseudo enum inheritance.

Let's say I define Marker class as follows:

public interface FieldInterface

Now I have have 5 enum classes that implement this interface.

enum Field1 implements FieldInterface
enum Field2 implements FieldInterface
enum Field3 implements FieldInterface
enum Field4 implements FieldInterface
enum Field5 implements FieldInterface

Each of the 5 enum "Field" class above defines a list of enums related to that field type. For example, enum Field1 might define enums: Field1A, Field1B, Field1C. Enum Field2 might define enums: Field2A, Field2B, Field2C

Now I need to convert plain text representation of each enum to the corresponding enum (similar to calling enum.valueOf(String)) without knowing which of the 5 enum classes the String belongs to.

One way to achieve this might be through reflection by first retrieving a list of all classes that implement said interface and iterating through all of the 5 enum classes until a match is found. However, I prefer to avoid reflection if possible (mainly due to performance reasons).

What other options are there to solve this problem?

Thanks

1 Answers1

2

A traditional implementation of a Map<String, Enum<?>> with a small complication of handling several enum classes. The static final map instance is defined in an interface and shared across all enums.

import java.util.HashMap;
import java.util.Map;
import java.util.stream.IntStream;

class Main {

    public static void main(final String[] args) {
        // Enum classes must be loaded
        loadEnums();

    System.out.println(X.get("FIELD1B"));
    ((X) () -> "FIELD1B").add();    // Attempt to pollute the map is blocked
    System.out.println(X.get("FIELD1B"));
    }

    private static void loadEnums() {
        IntStream.range(1, 10).mapToObj(n -> Main.class.getPackageName() + ".Main$Field" + n).forEach(Main::loadEnum);
    }

    private static void loadEnum(String name) {
        try {
            Class.forName(name);
        } catch (ClassNotFoundException e) {
        }
    }

    /**
     * All enums implement this marker interface and therefore share access to a static map from the name of the enum to its value.
     */
    interface X {
        Map<String, X> map = new HashMap<>();

        /**
         * This shared method uses enum's name method to get enum's string representation.
         */
        default void add() {
          if (this instanceof Enum<?>) {
              map.put(name(), this);
          }
        }

        static X get(String key) {
            return map.get(key);
        }

        /**
         * This method is always overwritten by each enum because all enums have a name() method.
         */
        String name();
    }

    enum Field1 implements X {
        FIELD1A, FIELD1B, FIELD1C;

        // We have to call add() to place each enum value in the shared map
        Field1() {
            add();
        }
    }

    enum Field2 implements X {
        FIELD2A, FIELD2B, FIELD2C;

        Field2() {
            add();
        }
    }
}
Delta George
  • 2,560
  • 2
  • 17
  • 11
  • 2
    This would be a better answer if you explained how it solves the problem. – VGR Apr 25 '22 at 22:26
  • @VGR Answer updated with comments that clarify the intent – Delta George Apr 25 '22 at 22:41
  • Updated answer with dynamic enum loading – Delta George Apr 26 '22 at 17:57
  • Simplified answer based on @Holger's excellent suggestion. – Delta George Apr 27 '22 at 14:54
  • 2
    You can make the creation of unintended implementations of `X` harder by also adding abstract methods `int ordinal();` and `Class extends Enum>> getDeclaringClass();` which are automatically implemented by `enum` types, so there’s no additional work for legitimate implementations but more obstacles for wrong subtypes. E.g., it can’t be implemented via lambda expression then. You could also change `this instanceof Enum>` to `getDeclaringClass().isInstance(this)` then to sharpen the contract. Of course, you could also declare `X` as `sealed` in recent Java versions. – Holger Apr 27 '22 at 15:26