Home > front end >  Java enum with user-friendly names Best Practices
Java enum with user-friendly names Best Practices

Time:12-24

Suppose I have a Java enum

public enum MyEnum {
    ITEM_1, ITEM_2, ITEM_3;
}

and my application requires the name of the enumeration in lower case. Which approach is a considered better approach?

  1. Have clients call toLowerCase() on the enumeration

MyEnum.ITEM_1.toString().toLowerCase(); // name().toLowerCase();
  1. Pass the lower case equivalent to the enum constructor

public enum MyEnum {
    ITEM_1("item_1"), ITEM_2("item_2"), ITEM_3("item_3");

    private final String enumVal;

    private MyEnum(String enumVal) {
        this.enumVal= enumVal;
    }

    public String getEnumVal() {
        return enumVal;
    }
}

I prefer the second approach because I feel it gives me more control. However, I admit that the verbosity of MyEnum seems to be too much for little payoff. Clients still have to call the method to get the lower case value of the enum: MyEnum.ITEM_1.getEnumVal. Another part of me is telling me that perpahs using enums for this is overkill and that the clients should simply pass the correct lowercase String values. That seems too error-prone so it is my least favorite alternative. I also seem to remember something I learn in school that goes something like "you should never force clients to resolve dependencies for your system". meaning that, I should not force clients to call toLowerCase() - in this case - simply because my system requires an input in lower case. Based on all those reasons, I am more inclined to choose option #2 over the first one. Alternatively, I could create a simple enum and go to all places in the system that requires those values in lower case and call toLowerCase() wherever is needed.

I understand the naming conventions used on this example are not good. But that's irrelevant for what I am asking. I also know that this might constitute an opinion-based question. Before you donwnvote the question for this reason, keep in mind that I am asking because I don't know that it is necessarily. There might be good reasons for choosing one approach over the other.

UPDATE:

I'm also considering (option #3) the following as an alternative to option #2

public enum MyEnum {
    ITEM_1, ITEM_2, ITEM_3;

    @Override
    public String toString() {
        return super.toString().toLowerCase();
    }
}

Which is much less verbose and accomplishes the same thing (in this specific case).

UPDATE #2:

The only advantage option #2 has over option #3 that I can see is that it preserves the original (upper case) name while also giving you the user-friendly (lower case) equivalent. With option #3, overriding toString() comes with the side effect of affecting the output of the name() method. Maybe this is the reason why the Javadoc states that overriding toString() might not be desirable.

CodePudding user response:

It is quite common for an enum to have an associated value(s).

For example a company that sells soda pop may have an enum indicating sweetener usage.

public enum Sweetener { WITH_SUGAR , NO_SUGAR , UNSWEETENED }

We want to associate a phrase for presentation to the user. So we add a field to hold a String. We mark it final to make it read-only. Optionally we add a getter method, but not required.

public enum Sweetener { 
    WITH_SUGAR ( "Sugar" ) , 
    NO_SUGAR  ( "Diet" ) , 
    UNSWEETENED ( "Unsweetened" ) 
    ;

    public final String label ;

    // Constructor
    public Sweetener ( Strung label ) {
        this.label = label ;
    }
}

Usage:

System.out.println( Sweetener.NO_SUGAR.label ) ;

Later on, the Marketing department decides “Diet” phrasing is passé, and “Zero Sugar” is in. Does it make sense for us to change the name of our enum from NO_SUGAR to ZERO_SUGAR, and then have to change all the places in our codebase where that enum name was used? No, of course that does not make sense. The meaning of our enum has not changed.

To accommodate the Marketing department’s decision, we simply change the phrasing of the label passed to our constructor.

…
NO_SUGAR  ( "Zero Sugar" ) ,   // Previously "Diet".
…

If the underlying meaning of our enum changes, then we should change the names. If our soda pop company decides to distinguish between natural cane sugar and high-fructose corn syrup, then we would need to add another enum object, and change the name of SUGAR.

I would argue that you are getting hung up on the fact that your enum’s associated label happens to be the same as the name you chose for your enum object’s name.

Indeed that coincidence makes me wonder if perhaps you should have worked harder to devise enum object names that get at the underlying truth rather than some then-common phrasing. Of course I don’t know that for sure as I don’t know your problem domain. I’ll just point to the wisdom seen in the example above where we deliberately named our object NO_SUGAR rather than the then-popular phrasing DIET.

So I suggest the option of storing the associated label as a member field on the enum. Access by way of public final field or a getter method.

The folks suggesting override of toString are misguided. The Object::toString method is meant for internal use by programmers and sys-admins, for logging and debugging. The text returned by that method should rarely, if ever, be used in your business logic.

CodePudding user response:

Your scenario is supported by overriding toString() in your enum type (your 3rd option). This eventuality is even mentioned in the Enum javadocs.

An enum type should override this method when a more "programmer-friendly" string form exists.

The original name (in uppercase) remains available to client code via name().

CodePudding user response:

What I recommend is another simple approach. You can just override the toString method. It is the simplest way:

@Override
public String toString() {
    return name().toLowerCase();
}

And you will get as following:

System.out.println(MyEnum.ITEM_1); // item_1
  • Related