Can I specify ordinal for enum in Java? - java

The ordinal() method returns the ordinal of an enum instance.
How can I set the ordinal for an enum?

You can control the ordinal by changing the order of the enum, but you cannot set it explicitly like in C++. One workaround is to provide an extra method in your enum for the number you want:
enum Foo {
BAR(3),
BAZ(5);
private final int val;
private Foo(int v) { val = v; }
public int getVal() { return val; }
}
In this situation BAR.ordinal() == 0, but BAR.getVal() == 3.

You can't set it. It is always the ordinal of the constant definition. See the documentation for Enum.ordinal():
Returns the ordinal of this enumeration constant (its position in its enum declaration, where the initial constant is assigned an ordinal of zero). 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.
And actually - you should not need to. If you want some integer property, define one.

As the accepted answer points out, you can't set the ordinal. The closest you can get to this is with a custom property:
public enum MonthEnum {
JANUARY(1),
FEBRUARY(2),
MARCH(3),
APRIL(4),
MAY(5),
JUNE(6),
JULY(7),
AUGUST(8),
SEPTEMBER(9),
OCTOBER(10),
NOVEMBER(11),
DECEMBER(12);
MonthEnum(int monthOfYear) {
this.monthOfYear = monthOfYear;
}
private int monthOfYear;
public int asMonthOfYear() {
return monthOfYear;
}
}
Note: By default, enum values start at 0 (not 1) if you don't specify values. Also, the values do not have to increment by 1 for each item.

You can update ordinal using reflection:
private void setEnumOrdinal(Enum object, int ordinal) {
Field field;
try {
field = object.getClass().getSuperclass().getDeclaredField("ordinal");
field.setAccessible(true);
field.set(object, ordinal);
} catch (Exception ex) {
throw new RuntimeException("Can't update enum ordinal: " + ex);
}
}

From http://download.oracle.com/javase/1.5.0/docs/api/java/lang/Enum.html
public final int ordinal()Returns the ordinal of this enumeration constant (its position in its enum declaration, where the initial constant is assigned an ordinal of zero). 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.
Returns:
the ordinal of this enumeration constant
If you have
public enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY
}
then SUNDAY has an ordinal of 0, MONDAY is 1, and so on...

Check out the Java Enum examples and docs
Returns the ordinal of this enumeration constant (its position in its enum declaration, where the initial constant is assigned an ordinal of zero). 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.

The easy answer: just change the order of the constants. The first defined will be 0, the second will be 1, etc. However, this may not be practical if you have constantly changing code, or enums will many many values. You can define a custom method to work around the default ordinal, but MAKE SURE it is well documented to avoid confusion!
public enum Values
{
ONE, TWO, THREE, FOUR;
public int getCustomOrdinal()
{
if(this == ONE)
{
return 3;
}
else if(this == TWO)
{
return 0;
}
...
}
}

Related

Guava getIfPresent() Incorrect return

I am trying to use guava's getIfPresent() for an enum as below:
private enum LegalColumns
{
PRODUCERS_CUT("PRODUCER'S CUT", PMCColumnsEnum.NETWORK_CUT_1.getColumnName()),
PROPOSED_LOCKED_CUT("PROPOSED LOCKED CUT", PMCColumnsEnum.NETWORK_CUT_3.getColumnName()),
LOCK("LOCK", PMCColumnsEnum.LOCKED_DELIVERY.getColumnName()),
FINAL_MIX("FINAL MIX", PMCColumnsEnum.MIX_DATE.getColumnName());
private String column;
private String replacementColumn;
LegalColumns(String column, String replacementColumn) {
this.column = column;
this.replacementColumn = replacementColumn;
}
public static LegalColumns getIfPresent(String column) {
System.out.println(Enums.getIfPresent(LegalColumns.class, column.trim().toUpperCase()));
return Enums.getIfPresent(LegalColumns.class, column.toUpperCase()).orNull();
}
}
When I step through this however, it always prints out Optional.absent() despite the strings being exact matches. I followed, to my knowledge, the guava spec exactly. Any ideas what I am missing?
https://google.github.io/guava/releases/15.0/api/docs/com/google/common/base/Enums.html#getIfPresent(java.lang.Class,%20java.lang.String)
Returns an optional enum constant for the given type, using
Enum.valueOf(java.lang.Class, java.lang.String). If the constant
does not exist, Optional.absent() is returned. A common use case is
for parsing user input or falling back to a default enum constant. For
example, Enums.getIfPresent(Country.class,
countryInput).or(Country.DEFAULT);
https://docs.oracle.com/javase/6/docs/api/java/lang/Enum.html?is-external=true#valueOf(java.lang.Class,%20java.lang.String)
Returns the enum constant of the specified enum type with the
specified name. The name must match exactly an identifier used to
declare an enum constant in this type. (Extraneous whitespace
characters are not permitted.)
From your comment
I have gone with another approach, but for others, I passed in columns like "FINAL MIX".
"FINAL MIX" != "FINAL_MIX"
Guava uses the enum identifier, not the string you pass into the constructor.
So for the enum instance, FINAL_MIX("FINAL MIX", PMCColumnsEnum.MIX_DATE.getColumnName()); the enum identifier is "FINAL_MIX" not the string you pass in "FINAL MIX"
IN ADDITION! as you do not define a Locale on your string.toUpperCase, you are at risk of the turkey I bug.

How do I define a method once and use it as a "building plan" for other methods?

I'm new to Java and have the following question:
Is there an easier way of making methods for each variable?
The meaning behind the question is:
Do I have to define a method that does the exact same thing as other methods except that they use different variable names and types?
I think the most common methods with the same problem are the get- and set-accessors:
They share the same structure, the only difference are the variable types and names.
In my following example, you need to know:
Variables varA-varD just represent the existance of multiple variables with various types and names.
The variable "inheritor" is special. I do NOT implement inheritance, but I DO have to verify that somewhere, maybe in the inheritor of the inheritor of the inheritor the same variable
has a value ("!= null"; in case of Lists, HashMaps, Enumerations, etc.)
or
has a value other than -2 (because 0 means nothing and -1 indicates "infinite" in my system, so I thought using -2 for indicating that the variable hasn't been set yet is a good idea; in case of Integers, Floats, Doubles, etc.).
I have verification methods...
...to check whether the variables have already been set (or not)
and for this reason the code is located
...outside of the setter because I have to check the variables even when they have not been set yet.
public class ExampleClass {
private int varA;
private String varB;
private ExampleEnum varC;
private List<OtherClass> varD;
//there are more variables here...
private ExampleClass inheritor;
public int getVarA() {
return varA;
}
public void setVarA(int varA) {
this.varA = varA;
}
public boolean validateVarA() {
//-2 is "not set" for Integers
if (varA == -2 && inheritor != null) {
return inheritor.getVarA() != -2;
} else {
return varA != -2;
}
}
//Do I have to define three methods for each variable?
//What if I had like 20 variables?!?
//I would need 60 methods altough every third one
//shares the same structure.
}
I needed some sort of "building plan" for a method:
public T getVar() {
return var;
}
public void setVar(T var) {
this.var = var;
}
public boolean verifyVar() {
//How would I get the invalid value for each type?
T invalidValue = ?;
if (var == invalidValue && inheritor != null) {
return inheritor.getVar() != invalidValue;
} else {
return var != invalidValue;
}
}
In the example above:
"Var" or "var" would be the variable name
and
"T" would be the type of var
I have no idea how I would get the invalid value...
Sorry in case I think too complicated and there is a simple answer to my question. Furthermore, I apologize for any grammar mistakes that may occur.
For generic getters and setters, there's always Map<String, Object>, but I'm pretty sure that's not what you want, so you should stick to the JavaBean conventions (as mentioned in the comments, any IDE would generate those for you and it makes total sense to have them according to OOP recommendations).
Any attempt to implement generic accessors would sooner or later become some java.util.Map with tones of reflection around it. If that's what you want, perhaps you should reconsider your model and switch your type-safe beans to some free-form types like map.
For validation, there's the javax.validation package (JSR-303).

Constant expressions from an Enum

Is there any way of converting an enum into a constant expression?
I want my switch operator to choose among the values of an enum, but I got a compile error "case expressions must be constant expressions", so I tried to declare it in a variable:
final int REG = MyEnum.REG.getIndex().intValue();
switch (service.getIndex()) {
case REG:
But I still get the same error. According to Oracle's documentation http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.28
A compile-time constant expression is an expression denoting a value
of primitive type or a String that does not complete abruptly and is
composed using only the following:
•Literals of primitive type and literals of type String
So it isn't working because I'm not using a literal. I think I will have to declare it as:
final int REG = 8;
But it'd be much better to link it to the enum. Is there any way of doing this?
EDIT
Turns out I don't need to use any final variable. It is just as simple as:
switch (service) {
case REG:
It didn't occur to me till I saw Andrea's comment. Thanks for your answers.
If possible, modify your getIndex() method so that it returns an enum instead of an integer. If this is not possible, you need to map the index to an enum element:
Given the following enum:
public enum Index {
ONE,
TWO,
THREE
}
you can map your index to an enum element by using
Index.values()[index]
Given your method Integer getIndex(), you can then do something like
switch(Index.values()[getIndex()])
case ONE : ...
break;
case TWO : ...
break;
case THREE : ...
break;
}
Note that this might throw an ArrayIndexOutOfBoundsException if you try to access an index within the enum which is larger than the number of enum elements (e.g. in the sample above, if getIndex() returns a value > 2).
I would encapsulate the expression Index.values()[getIndex()] into an enum method like valueOf(int index), similar to the default valueOf(String s). You can then also handle the valid array index check there (and for example return a special enum value if the index is out of range). Similarly, you can then also convert discrete values which have special meanings:
public enum Index {
ZERO,
ONE,
TWO,
THREE,
REG,
INVALID;
public static Index valueOf(int index) {
if (index == 8) {
return REG;
}
if (index >= values().length) {
return INVALID;
}
return values()[index];
}
}
This is an example only - in any case, it generally depends on the range of values you get from your getIndex() method, and how you want to map them to the enum elements.
You can then use it like
switch(Index.valueOf(service.getIndex())) {
case ZERO : ... break;
...
case REG : ... break;
...
}
See also Cast Int to enum in Java for some additional information (especially the hint that values() is an expensive operation since it needs to return a copy of the array each time it is called).
If you want specific numeral values assigned to enum constacts go like this
enum MyReg {
REG(8), OTHER(13);
public final int value;
MyReg(int value) {
this.value=value;
}
}
You then use it like this:
class Test {
public static void main(String[] args) {
MyReg reg = MyReg.REG;
switch (reg) {
case OTHER:
System.out.println(reg.value);
break;
case REG:
System.out.println(reg.value);
break;
}
}
}

How can't we compare two enum values with '<'?

If enum implements Comparable so why can't compare with < or >?
public class Dream
{
public static void main(String... args)
{
System.out.println(PinSize.BIG == PinSize.BIGGER); //false
System.out.println(PinSize.BIG == PinSize.BIG); //true
System.out.println(PinSize.BIG.equals(PinSize.BIGGER));//false
System.out.println(PinSize.BIG > PinSize.BIGGERER);// compilation error
//can't be compared
System.out.println(PinSize.BIG.toString().equals(PinSize.BIGGER));// #4
PinSize b = PinSize.BIG ;
System.out.println( b instanceof Comparable);// true
}
}
enum PinSize { BIG, BIGGER, BIGGERER };
You can do this:
PinSize.BIGGEST.ordinal() > PinSize.BIG.ordinal() // evaluates to `true`
Of course, assuming that BIGGEST was declared after BIG in the enumeration. The ordinal value in an enumeration is implicitly tied to the declaration order, by default the first value is assigned value 0, the second value 1 and so on.
So if yo declared the enumeration like this, things will work:
public enum PinSize {
SMALLEST, // ordinal value: 0
SMALL, // ordinal value: 1
NORMAL, // ordinal value: 2
BIG, // ordinal value: 3
BIGGER, // ordinal value: 4
BIGGEST; // ordinal value: 5
}
Implementing Comparable doesn't mean that you can use < or >. You can only use those with numeric values.
Implementing Comparable means that there's a compareTo() method. Try this:
System.out.println(PinSize.BIG.compareTo(PinSize.BIGGER));
The compareTo() method will return an int that is smaller than, equal to, or bigger than 0, depending on which value is "bigger". In the case of enum values, the "size" depends on the order of the enum value definitions.
The answers provided explain the problem well, but I would like to add my insights, because I feel that they don't answer question "why can't compare with < or >"?. The problem comes down to comparing references. PinSize.BIGGEST and PinSize.BIGGERER are reference variables. The same as the below:
String s;
int[] array;
MyObject myObject;
They represent addresses in memory. What is more, enums are singletons so there is always one object of the specified kind. Because of that the below line is allowed and returns true.
System.out.println(PinSize.BIG == PinSize.BIG); //true
Trying to check if one address in memory is greater or smaller than the other address in memory is impossible. Implementing Comparable interface and compareTo() method gives a chance to provide your own custom way of comparing objects not addresses in memory.
System.out.println(PinSize.BIG > PinSize.BIGGERER); // not possible

Setting values of enumerations, and parsing string to get an enum

My enum is like this currently:
public enum Manufacturers {
Honda,
GM,
Toyota,
Ferrari
}
I need to create a Hashmap so I plan on doing this, is this correct?
Manufacturers mfg = Manufacturers.Honda;
mfg.ordinal() // save as key
i.e. I will store the key using the enumInstance.ordinal()
Also, I need to be able to parse a string which will be the ordinal value of the enumeration, and get an enum back:
Manufacturers mfg = Manufacturers.valueOf(mfgOrdinalValueAsString);
The above gave me an error (the string was "1"). Is this the correct way? I guess I should have a try/catch in there right?
The .valueOf would actually be expecting the String "GM" (for 1).
As for storing your enum values in a map, use EnumMap which is designed specifically for this - and will be fast at it, too.
If you really wanted to reference a value by its ordinal, use something like Manufacturers.values()[1].
A suggestion: better use name() to get the name of the enum as a String, and whenever you need to get back the original Enum from it, use the valueOf() method - since valueOf() expects the name, not the ordinal, as a parameter. For example:
enum Example {ONE, TWO};
String name = Example.ONE.name();
Example e = Example.valueOf(Example.class, name); // e has value ONE
If you definitely need to use the ordinal, the ordinal() method will return an index which you can use to retrieve the respective Enum from the array returned by the values() method. Like this:
int ordinal = Example.ONE.ordinal();
Example e = Example.values()[ordinal]; // e has value ONE
As has already been pointed out, consider using EnumMap, as stated in the documentation, it is
A specialized Map implementation for use with enum type keys. All of the keys in an enum map must come from a single enum type that is specified, explicitly or implicitly, when the map is created. Enum maps are represented internally as arrays. This representation is extremely compact and efficient.
EDIT
If you need to associate a different code to each element of the enum (other than its automatically assigned ordinal), you can always add it as an attribute to the enum, together with getters and setters, like this:
public enum Manufacturers {
Honda(10),
GM(20),
Toyota(30),
Ferrari(40);
private int code;
Manufacturers(int code) {
this.code = code;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}
For example:
Manufacturers m = Manufacturers.Honda;
System.out.println(m.getCode()); // prints 10
m.setCode(100);
System.out.println(m.getCode()); // prints 100
Just be aware that you won't be able to reconstruct an Enum object from the code attribute, since that was defined by the programmer.

Categories

Resources