21

Several times I've seen people use title-case or even all lower-case naming for enum constants, for example:

enum Color {
  red,
  yellow,
  green;
}

This makes working with their string form simple and easy, if you want to do throw new IllegalStateException("Light should not be " + color + "."), for example.

This seems more acceptable if the enum is private, but I still don't like it. I know I can make an enum constructor with a String field, and then override toString to return that name, like this:

enum Color {
  RED("red"),
  YELLOW("yellow"),
  GREEN("green");

  private final String name;

  private Color(String name) { 
    this.name = name 
  }

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

But look how much longer that is. It's annoying to keep doing if you have a bunch small enums you want to keep simple. Is it okay to just use unconventional case formats here?

codebreaker
  • 1,694
  • 1
  • 18
  • 24
  • 4
    Its a convention. Do you have reason to break convention? That's up to you. –  May 19 '15 at 18:39
  • 11
    Just wondering what's wrong with an exception message that says `Light should not be RED.`. After all, this message is for the developer, the user should never see it. – Brandin May 19 '15 at 18:42
  • 1
    @Brandin if the color is more of an implementation detail, then no one should see it. Of course, then I guess that leaves the question of why we need a nice toString form. – codebreaker May 19 '15 at 18:46
  • 9
    At the end of the day it's up to you. If I was debugging your code and saw an exception message `Light should not be YELLOW.` I would guess it is a constant of some kind, the toString() method is just for debugging purposes so I don't see why you need to change how it looks in that message. – Brandin May 19 '15 at 18:51
  • 1
    I think the answer is more based in your idea of 'okay'. The world is, most likely, not going to burst into flames if you do this and you can even successfully write a program. Is it useful? meh too subjective. If you get some benefit, is it worth it for you and will it affect others? Those are the questions I'd care for. – That Realtor Programmer Guy May 19 '15 at 21:25
  • It would depend on who you are working for. Go by the code standers of your company or team. –  May 20 '15 at 02:20
  • Maybe not use the "sting form" of an enum? An enum is an enum and I think it's a bad design choice to have a stringrepresentation of it. – Pieter B May 21 '15 at 11:40
  • 1
    @PieterB I disagree. In java, enums are objects, and they're designed so that they can be used in an OO way. Implementing toString is part of that. – codebreaker May 21 '15 at 14:47
  • I'd say it's bad practice to make names used in code display names. What would you do to localise it, for example? (Or if a desired display name would not be a valid identifier name). – John B. Lambe Jan 22 '16 at 00:35

6 Answers6

17

The short answer is, of course, whether you want to break with naming conventions for what are essentially constants... Quoting from the JLS:

Constant Names

The names of constants in interface types should be, and final variables of class types may conventionally be, a sequence of one or more words, acronyms, or abbreviations, all uppercase, with components separated by underscore "_" characters. Constant names should be descriptive and not unnecessarily abbreviated. Conventionally they may be any appropriate part of speech.

The long answer, with regards to use of toString(), is that's definitely the method to override if you want a more readable representation of the enum values. Quoting from Object.toString() (emphasis mine):

Returns a string representation of the object. In general, the toString method returns a string that "textually represents" this object. The result should be a concise but informative representation that is easy for a person to read. It is recommended that all subclasses override this method.

Now, I'm not sure why some of the answers drifted to talking about converting enums to-and-fro with String values, but I'll just give my take here as well. Such serialization of enum values can easily be taken care of by using either the name() or ordinal() methods. Both are final and thus you can be sure of the returned values so long as the names or positioning of the values do not change. To me, that's a clear-enough marker.

What I gather from the above is: today, you might want to describe YELLOW as simply "yellow". Tomorrow, you might want to describe it as "Pantone Minion Yellow". These descriptions should be returned from calling toString(), and I wouldn't expect either name() or ordinal() to change. If I do, that's something I need to resolve within my codebase or my team, and becomes a greater question than just an enum naming style.

In conclusion, if all you intend to do is to log a more readable representation of your enum values, I'll still suggest sticking to conventions and then overriding toString(). If you also intend to serialize them into a data file, or to other non-Java destinations, you still have the name() and ordinal() methods to back you up, so there's no need to fret over overriding toString().

h.j.k.
  • 1,737
  • 1
  • 16
  • 20
6

Don't modify ENUM that's just bad code smell. Let an enum be an enum and a string be a string.

Instead, use a CamelCase converter for string values.

throw new IllegalStateException("Light should not be " + CamelCase(color) + ".");

There are many open source libraries that already solve this issue for Java.

http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/CaseFormat.html

Reactgular
  • 13,040
  • 4
  • 48
  • 81
  • Wouldn't it be non-deterministic in certain edge cases? (not a specific converter's behavior, but the general principle) – Panzercrisis May 19 '15 at 21:28
  • 1
    -1 Adding a third-party library that may not be what you want for basic String formatting may be overkill. – user949300 May 21 '15 at 15:35
  • 1
    @user949300 copy the code, write your own or what ever. You're missing the point. – Reactgular May 21 '15 at 15:54
  • Can you elaborate on "the point"? "Let an enum be an enum and a string be a string" sounds good, but what does it really mean? – user949300 May 21 '15 at 18:24
  • @user949300 the OP wanted to use a variable of type enum as if it was a string. Instead of altering the toString result of enum. He changes the enum implementation instead. It's like fixing a divide by zero error by overriding the divide operator on integers. – Reactgular May 21 '15 at 19:10
  • 1
    Enum provides type safety. He cannot pass "potatoe" as a color. And, perhaps, he includes other data in the enum, such as thr RGB value, just doesn't show it in the code. There are many good reasons to use an enum instead of a string. – user949300 May 21 '15 at 20:33
  • @user949300 what are you talking about? I'm not telling him to use a string instead of an enum. I give up. – Reactgular May 21 '15 at 20:54
4

Sending enums between my Java code and a database or client app, I often end up reading and writing the enum values as strings. toString() is called implicitly when concatenating strings. Overriding toString() on some enums meant that that sometimes I could just

"<input type='checkbox' value='" + MY_CONST1 + "'>"

and sometimes I had to remember to call

"<input type='checkbox' value='" + MY_CONST1.name() + "'>"

which led to errors, so I don't do that anymore. Actually, I don't override any methods on Enum because if you throw them around to enough client code, you'll eventually break someone's expectations.

Make your own new method name, like public String text() or toEnglish() or whatever.

Here is a little helper function that could save you some typing if you have lots of enums like the above:

public static String ucFirstLowerRest(String s) {
    if ( (s == null) || (s.length() < 1) ) {
        return s;
    } else if (s.length() == 1) {
        return s.toUpperCase();
    } else {
        return s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
    }
}

It's always easy to call .toUpperCase() or .toLowerCase() but getting back mixed-case can be tricky. Consider the color, "bleu de France." France is always capitalized, so you may want to add a textLower() method to your enum if you run into that. When you use this text at the beginning of a sentence, vs. the middle of a sentence, vs. in a title, you can see how a single toString() method is going to fall short. And that doesn't even touch characters that are illegal in Java identifiers, or that are a pain to type because they aren't represented on standard keyboards, or characters that don't have case (Kanji, etc.).

enum Color {
  BLEU_DE_FRANCE {
    @Override public String textTc() { return "Bleu De France"; }
    @Override public String textLc() { return "bleu de France"; }
  }
  CAFE_NOIR {
    @Override public String textTc() { return "Café Noir"; }
  }
  RED,
  YELLOW,
  GREEN;

  // The text in title case
  private final String textTc;

  private Color() { 
    textTc = ucFirstLowerRest(this.toString());
  }

  // Title case
  public String textTc() { return textTc; }

  // For the middle of a sentence
  public String textLc() { return textTc().toLowerCase(); }

  // For the start of a sentence
  public String textUcFirst() {
    String lc = textLc();
    return lc.substring(0, 1).toUpperCase() + lc.substring(1);
  }
}

It is not so difficult to use these properly:

IllegalStateException(color1.textUcFirst() + " clashes horribly with " +
                      color2.textLc() + "!")

Hopefully that also demonstrates why using mixed-case enum values will disappoint you as well. One last reason to keep with all-caps with underscores enum constants is that doing so follows the Principle of Least Astonishment. People expect it, so if you do something different, you are always going to have to be explaining yourself, or dealing with people misusing your code.

GlenPeterson
  • 14,890
  • 6
  • 47
  • 75
  • 3
    The suggestion to never override `toString()` on an enum makes no sense to me. If you want the guaranteed default behavior of enum names, use `name()`. I don't find it "tempting" at all to rely on `toString()` to provide the correct key for `valueOf(String)`. I guess if you want to idiot-proof your enums, that's one thing, but I don't think that's a good enough reason to recommend that you should *never* override `toString()`. – codebreaker May 20 '15 at 05:27
  • 1
    Furthermore, I found this, which recommends (as I was led to believe) that overriding toString on enums is in fact a good thing: http://stackoverflow.com/questions/13291076/java-enum-why-use-tostring-instead-of-name – codebreaker May 20 '15 at 05:33
  • 1
    @codebreaker toString() is called implicitly when concatenating strings. Overriding toString() on some enums meant that that sometimes I could just ``, and sometimes I had to remember to call ``, which led to errors. – GlenPeterson May 21 '15 at 10:53
  • Okay, that's a good point, but it only really matters when you're "serializing" enums with their name (which is not what I need to worry about with my example). – codebreaker May 21 '15 at 15:01
1

If there is the slightest chance that these string representations might be stored in places like databases or text files, then having them tied to the actual enum constants will become highly problematic if you ever need to refactor your enum.

What if one day you decide to rename Color.White to Color.TitaniumWhite while there are data files out there containing "White"?

Also, once you start converting enum constants to strings, the next step further down the road will be to generate strings for the user to see, and the problem here is, as I am sure you already know, that the syntax of java does not allow spaces within identifiers. (You will never have Color.Titanium White.) So, since you will probably need a proper mechanism for generating enum names to show to the user, it is best to avoid unnecessarily complicating your enum.

That having been said, you can of course go ahead and do what you have been thinking of doing, and then refactor your enum later, to pass names to the constructor, when (and if) you run into trouble.

You can shave a couple of lines off your enum-with-constructor by declaring the name field public final and losing the getter. (What is this love that java programmers have towards getters?)

Mike Nakis
  • 32,003
  • 7
  • 76
  • 111
  • A public final static gets compiled into code that uses it as the actual constant rather than a reference to the class that it comes from. This can cause a number of other fun errors when doing partial recompilation of code (or replacing a jar). If you are suggesting `public final static int white = 1;` or `public final static String white = "white";` (aside from the breaking of convention again), this loses the type safety that the enum provides. –  May 19 '15 at 20:38
  • @MichaelT Enum fields are not static. An instance is created for each enum constant, and the members of the enum are member variables of that instance. A `public final` instance field that gets initialized from the constructor behaves exactly like a non-final field, except that you are prevented ***at compile-time*** from assigning to it. You can modify it via reflection and *all* code that refers to it will immediately start seeing the new value. – Mike Nakis May 19 '15 at 20:45
1

Arguably, following the UPPERCASE naming convention violates DRY. (And YAGNI). e.g., in at your code example #2: "RED" and "red" are repeated.

So, do you follow official convention or follow DRY? Your call.

In my code, I will follow DRY. However, I will put in a comment on the Enum class, saying "not all uppercase because (explanation here)"

user949300
  • 8,679
  • 2
  • 26
  • 35
0

What if your Color is “darkish green” or “companyname orange” (yes, I have that in my code, it’s the exact orange that some customer wants, and it isn’t quite orange). So I want a name that is not a legal identifier in the language.

gnasher729
  • 42,090
  • 4
  • 59
  • 119