6

Say in some condition I want to initialize my enum Foo with the following values.

private enum Foo {
  BAR1("1"),
  BAR2("2"),
  BAR3("3")
}

In some other cases, I want a different set of values.

private enum Foo {
  BAR1("x"),
  BAR2("y"),
  BAR3("z")
}

Then later in other code, it can use the same enum for processing. How can I do this? Or is there other better approach doing achieve my goal?

3
  • 4
    AFAIK, enums are enumerated at compile-time, so I don't think this would be possible. You'd be better just defining an object with sensible accessors that provides the functionality you're after. Commented Oct 5, 2015 at 10:28
  • What are you trying to achieve by using those different values?. Commented Oct 5, 2015 at 10:30
  • "In some other cases" - what cases are these? How do you decide between them? Commented Oct 5, 2015 at 10:40

7 Answers 7

9

You can have multiple values in the enum initializer, and then logic to pick the appropriate value:

enum Foo {
  BAR1("1", "x"),
  BAR2("2", "y"),
  BAR3("3", "z");

  private final String first, second;

  private Foo(String first, String second) {
    this.first = first; this.second = second;
  }

  String value(boolean condition) {
    return condition ? first : second;
  }
}
Sign up to request clarification or add additional context in comments.

3 Comments

I like your idea. However, what the set of data grows to 3 sets or more. I would like to find a way that is more expandable. Not sure if your suggestion is the best way.
@Stan: have added a second answer, since the way I'd do it for more than 2 cases is quite different.
@Stan: Note that you didn't say that you needed more than 2 cases.
2

Write another class that wrapps the immutable part (your enum) with the mutable part (your state that can change over time).

A little bit like this:

enum Foo {
    BAR1, BAR2
}

class StatefulFoo {

    private Foo foo;
    private String name;

    public StatefulFoo(Foo foo, String name) {
        this.foo = foo;
        this.name = name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

1 Comment

Can you give some code? I sort of know what you mean, but I think it might be clearer with an example :)
1

I am adding this as a second answer rather than updating my existing answer as I think that it is too different to the answer that people have already upvoted.

An alternative to holding the options inside the class is to define mappings externally. This might be a better option if you want to be able to support more than two mappings.

The advantage of this is that you can define new mappings which are private to specific bits of your code: there is no need to change the definition of the enum when you add a new use case.

For example:

EnumMap<Foo, String> numberEnumMapping = new EnumMap<>(Foo.class);
numberEnumMapping.put(Foo.BAR1, "1");
numberEnumMapping.put(Foo.BAR2, "2");
numberEnumMapping.put(Foo.BAR3, "3");
Map<Foo, String> numberMapping = Collections.unmodifiableMap(numberEnumMapping);

EnumMap<Foo, String> letterEnumMapping = new EnumMap<>(Foo.class);
letterEnumMapping.put(Foo.BAR1, "x");
letterEnumMapping.put(Foo.BAR2, "y");
letterEnumMapping.put(Foo.BAR3, "z");
Map<Foo, String> letterMapping = Collections.unmodifiableMap(letterEnumMapping);

// ... More mappings.

(I'd personally use a Guava ImmutableMap for this, but you might not want to use Guava).

You can then pass around the Map<Foo, String> to the place where you need to perform the mapping:

void doSomething(Foo value, Map<Foo, String> mapping) {
  System.out.println(mapping.get(value));
}

You could define an interface rather than using the Map, if you think that is neater:

interface FooStrategy {
  String get(Foo value);
}

but the idea is the same: pass the FooStrategy to the place where you need to turn a Foo into a String.

void doSomething(Foo value, FooStrategy mapping) {
  System.out.println(mapping.get(value));
}

Comments

1

Since I'm not allowed to comment, here's an answer to @Andy

I have to say it first: the fact that an enum value may vary may be result of poor design. It's a bad practice and in multi-threaded envs, you may need to provide consistency over the whole enum, etc.

//Use this if your enum is system wide, and you make sure you can guarantee
//invariant -> length of array == length of enum.
static String [] sysWideArray = { "a", "b" };

enum Foo2 {
  BAR1,
  BAR2
  ;

  public String getS() { return sysWideArray[this.ordinal()]; }

}

static void printFoo2() {
  for(Foo2 f : Foo2.values()) {
    System.out.println("E: " + f + " getS()=" + f.getS());
  }
}

and in your main():

System.out.println("==================");
printFoo2();
System.out.println("==================");
sysWideArray = new String[] { "one", "two" };
printFoo2();

... so you have separated the enum from its mutable part

3 Comments

Effective Java 2nd ed Item 33 suggests that an EnumMap is preferable to ordinal indexing.
Note that it doesn't actually say that the enum values need to vary, just that they should map to different values under different circumstances. There are plenty of ways of doing this which aren't thread-unsafe, which your answer is because of the shared mutable state.
Didn't say otherwise, Andy. As I said, the problem is the approach to the solution
0

You could make the Enum constructor have 2 parameters, like BAR1("1","x"), and then create a static method in the Enum which will return one value or the other depending on a parameter.

Comments

0

Well, if I got it right you have Enum with Strings... so try Enum with constructor..

public enum Foo {
           BAR1("ONE"),
           BAR2("TWO")
           ;

           private final String text;

           /**
            * @param text
            */
           private Strings(final String text) {
               this.text = text;
           }

           /* (non-Javadoc)
            * @see java.lang.Enum#toString()
            */
           @Override
           public String toString() {
               return text;
           }
       }

keep in mind that enumerators must have different names, so BAR1("1") and BAR1("x") can not coexist in the same datatype...

1 Comment

"BAR1("1") and BAR("x") can not coexist in the same datatype" They've got different names.
0

Alternatively to this solution, you could create interface

interface FooConverter{
 String convert(Foo foo);
}

Then you can provide as many implementations as you need, and your enum might holds as many values as you need. You also could make it more generic, and then you wan't be limited to String and Foo

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.