Velvet Star Monitor

Standout celebrity highlights with iconic style.

news

Is it good practice to use ordinal of enum?

Writer Matthew Harrington

I have an enum:

public enum Persons { CHILD, PARENT, GRANDPARENT;
}

Is there any problem with using ordinal() method to check "hierarchy" between enum members? I mean - is there any disadvantages when using it excluding verbosity, when somebody can change accidentally order in future.

Or is it better to do something like that:

public enum Persons { CHILD(0), PARENT(1), GRANDPARENT(2); private Integer hierarchy; private Persons(final Integer hierarchy) { this.hierarchy = hierarchy; } public Integer getHierarchy() { return hierarchy; }
}
11

11 Answers

TLDR: No, you should not!

If you refer to the javadoc for ordinal method in Enum.java:

Most programmers will have no use for this method. It is designed for use by sophisticated enum-based data structures, such as java.util.EnumSet and java.util.EnumMap.

Firstly - read the manual (javadoc in this case).

Secondly - don't write brittle code. The enum values may change in future and your second code example is much more clear and maintainable.

You definitely don't want to create problems for the future if a new enum value is (say) inserted between PARENT and GRANDPARENT.

11

As suggested by Joshua Bloch in Effective Java, it's not a good idea to derive a value associated with an enum from its ordinal, because changes to the ordering of the enum values might break the logic you encoded.

The second approach you mention follows exactly what the author proposes, which is storing the value in a separate field.

I would say that the alternative you suggested is definitely better because it is more extendable and maintainable, as you are decoupling the ordering of the enum values and the notion of hierarchy.

0

The first way is not straight understandable as you have to read the code where the enums are used to understand that the order of the enum matters.
It is very error prone.

public enum Persons { CHILD, PARENT, GRANDPARENT;
}

The second way is better as it is self explanatory :

CHILD(0),
PARENT(1),
GRANDPARENT(2);
private SourceType(final Integer hierarchy) { this.hierarchy = hierarchy;
}

Of course, orders of the enum values should be consistent with the hierarchical order provided by the enum constructor arguments.

It introduces a kind of redundancy as both the enum values and the arguments of the enum constructor conveys the hierarchy of them.
But why would it be a problem ?
Enums are designed to represent constant and not frequently changing values.
The OP enum usage illustrates well a good enum usage :

CHILD, PARENT, GRANDPARENT

Enums are not designed to represent values that moves frequently.
In this case, using enums is probably not the best choice as it may breaks frequently the client code that uses it and besides it forces to recompile, repackage and redeploy the application at each time an enum value is modified.

3

First, you probably don't even need a numeric order value -- that's what Comparableis for, and Enum<E> implements Comparable<E>.

If you do need a numeric order value for some reason, yes, you should use ordinal(). That's what it's for.

Standard practice for Java Enums is to sort by declaration order, which is why Enum<E> implements Comparable<E> and whyEnum.compareTo() is final.

If you add your own non-standard comparison code that doesn't useComparable and doesn't depend on the declaration order, you're just going to confuse anyone else who tries to use your code, including your own future self. No one is going to expect that code to exist; they're going to expect Enum to be Enum.

If the custom order doesn't match the declaration order, anyone looking at the declaration is going to be confused. If it does(happen to, at this moment) match the declaration order, anyone looking at it is going to come to expect that, and they're going to get a nasty shock when at some future date it doesn't. (If you write code (or tests) to ensure that the custom order matches the declaration order, you're just reinforcing how unnecessary it is.)

If you add your own order value, you're creating maintenance headaches for yourself:

  1. you need to make sure your hierarchy values are unique
  2. if you add a value in the middle, you need to renumber all subsequent values

If you're worried someone could change the order accidentally in the future, write a unit test that checks the order.

In sum, in the immortal words of Item 47:know and use the libraries.


P.S. Also, don't use Integer when you mean int. 🙂

5

If you only want to create relationships between enum values, you can actually use the trick of using other enum values:

public enum Person { GRANDPARENT(null), PARENT(GRANDPARENT), CHILD(PARENT); private final Person parent; private Person(Person parent) { this.parent = parent; } public final Parent getParent() { return parent; }
}

Note that you can only use enum values that were declared lexically before the one you're trying to declare, so this only works if your relationships form an acyclic directed graph (and the order you declare them is a valid topological sort).

Using ordinal() is unrecommended as changes in the enum's declaration may impact the ordinal values.

UPDATE:

It is worth noting that the enum fields are constants and can have duplicated values, i.e.

enum Family { OFFSPRING(0), PARENT(1), GRANDPARENT(2), SIBLING(3), COUSING(4), UNCLE(4), AUNT(4); private final int hierarchy; private Family(int hierarchy) { this.hierarchy = hierarchy; } public int getHierarchy() { return hierarchy; }
}

Depending on what you're planning to do with hierarchy this could either be damaging or beneficial.

Furthermore, you could use the enum constants to build your very own EnumFlags instead of using EnumSet, for example

I would use your second option (using a explicit integer) so the numeric values are assigned by you and not by Java.

Let's consider following example:

We need to order several filters in our Spring Application. This is doable by registering filters via FilterRegistrationBeans:

 @Bean public FilterRegistrationBean compressingFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(compressingFilter()); registration.setName("CompressingFilter"); ... registration.setOrder(1); return registration; }

Let's assume we have several filters and we need to specify their order (e.g. we want to set as first the filter which add do MDC context the JSID for all loggers)

And here I see the perfect usecase for ordinal(). Let's create the enum:

 enum FilterRegistrationOrder { MDC_FILTER, COMPRESSING_FILTER, CACHE_CONTROL_FILTER, SPRING_SECURITY_FILTER, ... }

Now in registration bean we can use:registration.setOrder(MDC_FILTER.ordinal());

And it works perfectly in our case. If we haven't had an enum to do that we would have had to re-numerate all filters orders by adding 1 to them (or to constants which stores them). When we have enum you only need to add one line in enum in proper place and use ordinal. We don't have to change the code in many places and we have the clear structure of order for all our filters in one place.

In the case like this I think the ordinal() method is the best option to achieve the order of filters in clean and maintainable way

According to java doc

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.

You can control the ordinal by changing the order of the enum, but you cannot set it explicitly.One workaround is to provide an extra method in your enum for the number you want.

enum Mobile { Samsung(400), Nokia(250),Motorola(325); private final int val; private Mobile (int v) { val = v; } public int getVal() { return val; }
}

In this situation Samsung.ordinal() = 0, but Samsung.getVal() = 400.

This is not a direct answer to your question. Rather better approach for your usecase. This way makes sure that next developer will explicitly know that values assigned to properties should not be changed.

Create a class with static properites which will simulate your enum:

public class Persons { final public static int CHILD = 0; final public static int PARENT = 1; final public static int GRANDPARENT = 2;
}

Then use just like enum:

Persons.CHILD

It will work for most simple use cases. Otherwise you might be missing on options like valueOf(), EnumSet, EnumMap or values().

2

You must use your judgement to evaluate which kind of errors would be more severe in your particular case. There is no one-size-fits-all answer to this question. Each solution leverages one advantage of the compiler but sacrifices the other.

If your worst nightmare is enums sneakily changing value: use ENUM(int)

If your worst nightmare is enum values becoming duplicated or losing contiguousness: use ordinal.

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy