4

I have an interface:

public interface NamedEnum {
    String getName();
}

An enum which implements the interface:

public enum MyEnum implements NamedEnum {
    
    MYVALUE1("My value one"),
    MYVALUE2("My value two");
    
    private String name;
    
    private MyEnum(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

A method which doesn't compile:

public static Map<Integer,String> wrong(Enum<? extends NamedEnum> value) {
    Map<Integer,String> result = new LinkedHashMap<Integer, String>();
    for (Enum<? extends NamedEnum> val : value.values())
        result.put(val.ordinal(), val.getName());
    return result;
}

Two errors:

The method values() is undefined for the type Enum<capture#1-of ? extends NamedEnum>

The method getName() is undefined for the type Enum<capture#3-of ? extends NamedEnum>

I can't figure out how the method above can accept an enum which implements the interface.

3
  • 1
    It should probably be pointed out that you may not want to use ordinal. Quoting the Javadoc: "Most programmers will have no use for this method. It is designed for use by sophisticated enum-based data structures, such as EnumSet and EnumMap." You've not said what you're doing, but take heed. Also, check out Effective Java 2nd ed Item 33: "Use EnumMap instead of ordinal indexing". Commented Dec 22, 2016 at 17:22
  • "You've not said what you're doing": I thought it would be pointless but it wasn't. Ok... reset! I jumped from Java6 to 8 and I missed some in between. I wasn't aware of multple bound in generics but I already saw the ordinal quote in the javaDoc but I was lazy and didn't check the EnumSet/Map: my bad. I need my enum to build select/options like this <option value="0">My value one</option> and so on... I understand I can do it simply like this instead <option value="MYVALUE1">My value one</option> and get the values with valueOf("MYVALUE1"). Commented Dec 23, 2016 at 8:43
  • There is nothing here that is new since Java 6 (see Intersection types in JLS). Also, EJ 2nd Ed was written for Java 6. But yes, for what you say you're doing, using the enum names is a lot better than the ordinals. Commented Dec 23, 2016 at 9:38

2 Answers 2

9

Define a type variable with an intersection type:

public static <E extends Enum<E> & NamedEnum> Map<Integer,String> wrong(E value) {

But note that this is passing an element of the enum, not the enum class itself. You might want to use:

public static <E extends Enum<E> & NamedEnum> Map<Integer,String> wrong(Class<E> clazz) {

and then use clazz.getEnumConstants() to get the values to iterate over:

for (E val : clazz.getEnumConstants())

Alternatively, you can use value.getDeclaringClass().getEnumConstants().

Sign up to request clarification or add additional context in comments.

Comments

3

While I agree with the solution suggested by Andy, I think you may also want to look at the problem differently.

IIUC, you are looking for a way to get the value returned by getName() based on the enum's ordinal. In other words, you're looking for a mapping such as this:

  • 0 => "My Value one"
  • 1 => "My value two"

If that's indeed the case then how about using the array of enum values directly (the array is indexed by the enum ordinal values). For instance:

NamedEnum[] arr = MyEnum.values();
System.out.println(arr[0].getName());   // Output: "My value one"
System.out.println(arr[1].getName());   // Output: "My value two"

Note that the arr variable can be assigned from different enums (as long as they implement the NamedEnum interface). For instance, if you have this other enum defined in your program:

public enum MyOtherEnum implements NamedEnum {

  MYVALUE3("My other value three"),
  MYVALUE4("My other value four");

  private String name;

  private MyOtherEnum(String name) {
      this.name = name;
  }

  @Override
  public String getName() {
      return name;
  }
}

Then the following is a valid main() method:

public static void main(String[] args) {
  NamedEnum[] arr = MyEnum.values();
  System.out.println(arr[0].getName()); 
  System.out.println(arr[1].getName()); 

  arr = MyOtherEnum.values();
  System.out.println(arr[0].getName()); 
  System.out.println(arr[1].getName());
}

The output, as expected is:

My value one
My value two
My other value three
My other value four

Hope that helps.

6 Comments

This allows you to mix up the contents of the array from different enums: new NamedEnum[] { MyEnum.MYVALUE1, MyEnum2.MYVALUE3 }. Whether that's important or not depends upon the application.
"you are looking for a way to get the value returned by getName() based on the enum's ordinal" not exactly the same... I need either nameand ordinal. What if the Java Enum implementation will change allowing assignable ordinals? Just a wild guess but some language allows it (Delphi does, for example). Thank you for your answer. Upvoted
@AndyTurner - Based on the code of the wrong() method (posted at the question), once the result variable is returned the calling code ends up with a Map<Integer,String> which is just as prone to this type of mix up.
@ItayMaman it's up to the caller to look after the value returned; it is "correct" when they get it back. But if you can pass in MYVALUE1 and MYVALUE3, your code would return a map of size 1, since they both have the same ordinal; that is probably not the correct result.
@AndyTurner I am not suggesting a method to which one can pass enum constants. I am suggesting that if all he wants is to look up enum constant by ordinal then the standard .values() method returns an array which can be used just for this purpose.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.