Darkman
Darkman

Reputation: 323

Java Generic Enum as a Parameter

So I have several Enums defined like the following. I have a method that uses the Enum to find the value.

public enum MyEnum1
{
   STRING1("My String1"),
   STRING2("My String2"),
   STRING3("My String3"),
   STRINGLESS("String not found");

   private String s;

   private MyEnum1(String s) { this.s = s; }

   public String getValue() { return s; }
}

public enum MyEnum2
{
   STRING1("My String1"),
   STRING2("My String2"),
   STRING3("My String3"),
   STRINGLESS("String not found");

   private String s;

   private MyEnum2(String s) { this.s = s; }

   public String getValue() { return s; }
}

public class MyClass1
{
    public static String getPlacement(String myString)
    {
        for (MyEnum1 a: MyEnum1.values())
            if (myString.contains(a.toString()))
                return a.getValue();

        return MyEnum1.STRINGLESS.getValue();
     }
}

public class MyClass2
{
    public static String getPlacement(String myString)
    {
        for (MyEnum2 a: MyEnum2.values())
            if (myString.contains(a.toString()))
                return a.getValue();

        return MyEnum2.STRINGLESS.getValue();
     }
}

Right now I have 5 Enums defined that all get processed by the same getPlacement method but I had to create 5 different MyClass classes (MyClass2, MyClass3...) with MyEnum2, MyEnum3... hard coded in the For Loop to make it happen.

I've tried...

public static String getPlacement(Enum e, String myString) {}

and

public static String getPlacement(Enum<?> e, String myString) {}

but neither works.

I would like to pass an Enum to the getPlacement method as a parameter thus allowing me to only have one MyClass class that could process the 5 different Enums. Is this possible?

Upvotes: 3

Views: 12767

Answers (3)

Radiodef
Radiodef

Reputation: 37845

To preserve the exact semantics of your getPlacement, you can do it like this:

interface StringValue {
    String getValue();
}

enum MyEnum1 implements StringValue {
    ...
    private String stringValue;
    ...

    @Override
    public String getValue() {
        return stringValue;
    }
}

static <E extends Enum<E> & StringValue>
String getPlacement(Class<E> enumClass, String inValue) {
    for(E constant : enumClass.getEnumConstants()) {
        if(inValue.contains(constant.toString()))
            return constant.getValue();
    }
    return Enum.valueOf(enumClass, "STRINGLESS").getValue();
}

I am not sure if the exact semantics here are actually important to you. If they're not, there are simpler ways. If you don't need contains you can just use Enum#valueOf. If you can override toString (because you can also use Enum#name) then you don't need an interface.

And also, like all complicated situations with enum, things may be much simpler for you if you just do not use enum. A final class with a private constructor and public static final constants can do many things that enum can not (like inherit from an abstract supertype...).

Upvotes: 6

jaco0646
jaco0646

Reputation: 17066

The Enum base class java.lang.Enum has a static factory method valueOf(Class, String) for constructing an arbitrary enum instance. With a bit of generics magic you should be able to solve your problem.

public enum MyEnum1 {
   STRING1("MyEnum1 String1"),
   STRING2("MyEnum1 String2"),
   STRING3("MyEnum1 String3"),
   STRINGLESS("MyEnum1 String not found");

   private String s;
   private MyEnum1(String s) { this.s = s; }

   @Override
   public String toString() { return s; }
}

public enum MyEnum2 {
   STRING1("MyEnum2 String1"),
   STRING2("MyEnum2 String2"),
   STRING3("MyEnum2 String3"),
   STRINGLESS("MyEnum2 String not found");

   private String s;
   private MyEnum2(String s) { this.s = s; }

   @Override
   public String toString() { return s; }
}

public static void main(String[] args) {
    System.out.println(getPlacement(MyEnum1.class, "STRING1").name());
    System.out.println(getPlacement(MyEnum2.class, "STRING3").name());
    System.out.println(getPlacement(MyEnum2.class, "foobar").name());
}

public static <T extends Enum<T>> Enum<T> getPlacement(Class<T> clazz, String value) {
    try {
        return Enum.valueOf(clazz, value);
    } catch (IllegalArgumentException e) {
        return Enum.valueOf(clazz, "STRINGLESS");
    }
 }

Upvotes: 0

topr
topr

Reputation: 4612

Add such interface to your code.

public interface Named {
    String name();
}

Let all of your enums implement it like

public enum MyEnum1 implements Named {...}

Then declare your method with interface like:

public static String getPlacement(Named enumValue, String myString) {}

Inside of the getPlacement() method call enumValue.name() instead of toString(), although you can do the exact same with toString(), depends what you gonna use in your interface definition (which can be named as you like, not necessarily Named).

By the way, using single letters as variable names is usually considered a bad practice.

Upvotes: 1

Related Questions