Is it good practice to use ordinal of enum?

47,092

Solution 1

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.

Solution 2

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.

Solution 3

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.

Solution 4

First, you probably don't even need a numeric order value -- that's what Comparable is 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 why Enum.compareTo() is final.

If you add your own non-standard comparison code that doesn't use Comparable 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. 🙂

Solution 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).

Share:
47,092
ByeBye
Author by

ByeBye

Java enthusiast.

Updated on July 09, 2022

Comments

  • ByeBye
    ByeBye almost 2 years

    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;
        }
    
    }
    
  • David Conrad
    David Conrad about 7 years
    This is a good point but I think it would be just as good to simply add a comment that the order of the identifiers is significant.
  • prashant
    prashant about 7 years
    How is having to manually renumber all subsequent items when you insert a new item in the list more maintainable?
  • Leonardo Pina
    Leonardo Pina about 7 years
    enum fields may be used to other things besides comparison and order.
  • prashant
    prashant about 7 years
    @LeonardoPina Yes, but that's not what the OP asked.
  • Leonardo Pina
    Leonardo Pina about 7 years
    To be fair, it was unclear what OP wanted to do with 'hierarchy'.
  • prashant
    prashant about 7 years
    It's not a particularly well-written question, but the only use it explicitly talks about is order, and the example given exactly hard-codes the same values ordinal() would give.
  • vikingsteve
    vikingsteve about 7 years
    @DavidMoles - good question. Since you are explicitly declaring hierarchy, its clear you are associating an Integer with each enum. Most importantly, if the enums are somehow re-ordered (say, someone adds GUARDIAN on the line between PARENT and GRANDPARENT) then the value for the last enum value won't change.
  • Chris Hayes
    Chris Hayes about 7 years
    @DavidMoles It may not be the most maintainable, but it's clearly better than having your behavior depend on the order in which you happen to have declared your enum values.
  • nhouser9
    nhouser9 about 7 years
    @DavidMoles because the only thing less maintainable is code that will break if anyone changes it in the wrong way, and which adds nothing to explain that fact. Though personally, I would use the first method and adding a comment explaining that order matters...
  • Dan Is Fiddling By Firelight
    Dan Is Fiddling By Firelight about 7 years
    @nhouser9 ... and a set of tests that will fail if someone does ignore the comment and changes the order. If you're in the unfortunate position of having a team where unit tests are not run or failures are frequently ignored and implementation that explicitly numbers them is much safer.
  • prashant
    prashant about 7 years
    @ChrisHayes Lots of things depend on the order in which you happen to have declared your enum values, including compareTo(), which is final. Order is meaningful for Java enums. If you want it not to be meaningful, you should write your own enum-like class with your own ordering rules.
  • prashant
    prashant about 7 years
    @vikingsteve If the intent is to allow multiple instances to share the same level, then clearly ordinal() wouldn't work in the first place.
  • Robin Davies
    Robin Davies about 6 years
    The huge downside of that being that arguments for method declarations are of type int, and there is no longer compiler protection against passing an illegal value. This seems like a disproportionate price to pay for avoiding the incredibly subtle downside of using ordinal.
  • Robin Davies
    Robin Davies about 6 years
    Except the second way really isn't self-explanatory.
  • davidxxx
    davidxxx about 6 years
    @Robin Davies you refer to hierarchy field name that may be misleading ?
  • Robin Davies
    Robin Davies about 6 years
    @Davide Moles. Thank you. A voice of reason in a sea of obnoxious and counterproductive lore . One always struggles with the sort of advice that's been given on this question. I spent two days mulling it over, and eventually came to the same conclusion you did. And was on my way back to write an argument against the use of enum fields in this case, when I found your post. If only I'd noticed it two days ago.
  • Psyrus
    Psyrus almost 6 years
    There are many cases where to avoid ordinal, and generally is a maintenance issue due to inherent ambiguity of just "ordinal" within any context. Joshua Bloch in Effective Java discusses this, as well as many academic articles/ papers
  • olyv
    olyv over 5 years
    IMO, that's for enums exist
  • Peeyush
    Peeyush over 5 years
    Side note : misuse of ordinal within java is widespread. I agree with above answer
  • Hatefiend
    Hatefiend over 3 years
    @vikingsteve Ordinal is a very powerful function. For example if you had a MONTH Enum instance passed to a function which had to print out what # month of the year that month was, the only viable option available would be to use the ordinal() function. Calling values() is not typically a good practice as it must create an additional copy of the array on every call, which is O(n) space and complexity. Beginners could get the wrong impression from your answer.
  • Bruno L.
    Bruno L. almost 3 years
    By default, HQL sets enumeration values using the ordinal() method in table columns.