Use enums for array indexes in Java
While reading Effective Java I came across the suggestion to "use enums instead of int
constants". In a current开发者_如何学运维 project I am doing something similar to that below:
int COL_NAME = 0;
int COL_SURNAME = 1;
table[COL_NAME] = "JONES"
How would I use enums instead to achieve this? Due to the interface I'm forced to use, I must use an int
for my index. The example above is just an example. I'm actually using an API that takes an int
for index values.
Applying one usefull pattern together with an anti-pattern often fails ;-)
In your case using an array for not-really array-like data provides a problem when you want to replace int
constants with enum
values.
A clean(er) solution would be something like an EnumMap
with the enum
values as keys.
Alternatively you could use table[COL_NAME.ordinal()]
if you absolutely must.
If some API forces you to pass around int
values but you have control over the actual values (i.e. you could pass your own constants), then you could switch to using enum
values in your code and convert to/from enum
only at the places where your code interfaces with the API. The reverse operation of enumValue.ordinal()
is EnumClass.values()[ordinal]
).
It sounds like you are trying to use a EnumMap. This is a Map which wraps an array of values.
enum Column {
NAME, SURNAME
}
Map<Column, String> table = new EnumMap<Column, String>(Column.class);
table.put(Column.NAME, "JONES");
String name = table.get(Column.NAME);
This would much simpler if you used a POJO.
classPerson {
String name;
String surname;
}
Person person = new Person();
person.name = "JONES";
String name = person.name;
Depends on the requirements. You could use Enum.ordinal()
to convert an enum to an int.
Note that it's not possible to pass an Enum directly as the index.
Edit:
Another possibility would be to use a Map<YourEnum, String> map
and then use map.get(EnumValue)
.
Since you have to use that table and thus can't actually use an EnumMap
, I personally think the best solution would be to stick with what you have. In an enum, the ordinal values of the elements are not supposed to have any intrinsic meaning, while in your case they do, since they are used as indices into the table.
The problem you have is not that you are not using enums, it's that you need magic values to extract column data out of a table. In this case, using integer constants is the right tool for the job, unless if you can tackle the underlying problem of that table.
Now you could tackle it by wrapping the table in your own class that accesses it, and use enums in and out that class. But this introduces more code, another layer of indirection and doesn't actually solve any problems for you, except that an enum is a bit easier to maintain than a list of int values (greatly offset by you having to maintain the wrapper you now wrote).
You could consider this work if you are writing a public API that other people will use, since it will avoid having them depend on some magic values that might change over time (tight coupling which will break things). If you are, then a wrapper which uses an EnumMap
internally is likely the way to go.
Otherwise, leave it as is.
Depending on the values of your array, you might be looking at an object instead. For examples if your array looks something like this
person[FIRST_NAME] = "Jim Bob"
person[SURNAME] = "Jones"
person[ADDRESS] = "123 ABC St"
person[CITY] = "Pleasantville"
...
Then what you really want is something like this
Person jimBob = new Person("Jim Bob", "Jones");
jimBob.setAddress("123 ABC St", "Pleasantville", "SC");
....
See Refactoring: Replace Array with Object
I have also run into this problem, and having come from the C++ world I wasn't expecting to run into this.
The beauty of enum is that can create a series of related constants with unique values where the actual value is irrelevant. Their use makes code easier to read and guards against variables being set to invalid enum values (especially in Java), but in a situation like this it is a major pain. While you might have "enum Pets { CAT, BIRD, DOG }" and don't really care what value CAT, BIRD, and DOG actually represent, it's so nice and clean to write: myPets[CAT] = "Dexter"; myPets[BIRD] = "Polly"; MyPets[DOG] = "Boo-Rooh";
In my situation I ended up writing a conversion function where you pass in an enum value and I return a constant. I hate doing this with a passion, but it's the only clean way I know to maintain the convenience and error checking of enum.
private int getPetsValue(Pets inPet) {
int value = 0;
switch (inPet) {
case CAT: value = 0; break;
case BIRD: value = 1; break;
case DOG: value = 2; break;
default:
assert(false);
value = 0;
break;
}
return value;
}
You can define an enum
with a constructor like so:
public enum ArrayIndex {
COL_NAME(0), COL_SURNAME(1);
private int index;
private ArrayIndex(int index) {
this.index = index;
}
public int getIndex() {
return this.index;
}
};
And then use it like this:
public static void main (String args[]) {
System.out.println("index of COL_NAME is " + ArrayIndex.COL_NAME.getIndex());
}
精彩评论