tags:

views:

111

answers:

3

I have an enum that looks a little bit like this:

public enum Numbers {
  ONE(1), TWO(2), THREE(3);

  public final int num;

  public Numbers(int num) {
    this.num = num;
  }
}

I want to be able to convert from argument to enum, for instance from the int 1 to the enum ONE. Is there any built-in mechanism in Java Enums to do this, or do I have to write my own logic for it?

+8  A: 

Yes you have to write your own logic as the num variable is a part of your own logic :

public enum Numbers {
    ONE(1), TWO(2), THREE(3);

    public final int num;

    private Numbers(int num) {
        this.num = num;
    }

    public static Numbers getNumber(int i){
        for(Numbers number : Numbers.values()){
            if(i == number.num){
                return number;
            }
        }
        throw new IllegalArgumentException("This number doesn't exist");
    }
}
Colin Hebert
this supports a more general problem than my answer (basically, whatever indexing system one likes), but has some performance trade-offs: incremental searching for matching values every lookup call, memory for the index, and iirc creation of the `values()` array every lookup call.
Carl
@Carl, Yeah, I suppose a solution would be to have a switch statement on `i` and a `return` based on the value of `switch`. I would be so much faster. But yet so ugly.
Colin Hebert
also, if you're going to do it this way, why not just use a `Map<Integer,Number>`, where the map is statically created?
Carl
@Carl, As I said, I found the solution in my previous comment, this is getting uglier :P
Colin Hebert
+1  A: 

If you want conversion from the ordinal you have to do it yourself. There is however automatic conversion from the name of an enum. Btw there is no need to specify the ordinal, that is done automatically and it starts with 0 and there is a ordinal() getter.

Enum.valueOf(Numbers.class, "ONE")

would return Numbers.ONE

willcodejavaforfood
A: 

If you can start your particular enum with ZERO instead, you could do

// ...
private static final Numbers[] list = values();
public static Numbers get(int which) { return list[i]; }
// ...

and ignore assigning indices to your enum.

EDIT: refactor safe option:

//..
private static final Map<Integer, Numbers> getter; static {
 Numbers[] ns = values();
 getter = new HashMap<Integer, Numbers>(ns.length, 1f);
 for (Numbers n : ns) getter.put(n.num, n);
}
public static Numbers get(int which) { return getter.get(which); }
//..

this is also conducive to changing your index to whatever type you like and returns null instead of throwing an exception if you ask for garbage (which can be preferable).

Carl
This isn't a great idea, since as well as being slightly fragile, it relies on the enum constants' **ordinal** values, which have nothing to do with the `num` field. If one defined the enum as `public enum Numbers { THREE(3), TWO(2), ONE(1), ZERO(0) }`, which is perfectly legal, your code would return the wrong values.
Andrzej Doyle
@Andrzej: it sure does rely on that fact, and yes relying on that is fragile. But if the problem is simple enough (a total judgment call), the additional flexibility might not be worth the performance hit of more flexible solutions.
Carl
I'll take the (probably trivial) performance hit, over code that breaks during "safe" refactorings, any day. If improved performance is the only reason to recommend this approach, then Knuth has something to say to you.
Andrzej Doyle
@Andrzej: more important than performance, my first answer is very easy (trivial, I'd say) to implement, compared to the refactor-safe options. I'd go with this on the first pass, and note a brief "TODO: implement refactor-safe option" if non-trivial refactoring ever comes up. Yes, I might end up repeating work, but if the problem really is simple and I never refactor, I spent less time and get better performance. Like...lazy instantiation, only for design.
Carl