63

I have read the question Difference of Enum between java and C++? but I'm still confused.

I would like the following to return the related String:

public enum Checker {
    EMPTY ("Empty"),
    RED ("Red"),
    YELLOW ("Yellow");
}

From what I have read, this should be possible. Just would like you to shed some light on it on how to implement it.

Community
  • 1
  • 1
iTEgg
  • 8,212
  • 20
  • 73
  • 107

2 Answers2

122

Short Answer

You need a constructor, a field and a getter.

Constructors

Enum types can have constructors, provided that their access level is either private or default (package-private). You can not directly call these constructors, except in the enum declaration itself. Similar to classes, when you define an enum constant without parameters, you actually call the default constructor generated by the compiler. E.g.

public enum King {
    ELVIS
}

is equivalent to

public enum King {
    ELVIS() // the compiler will happily accept this
}

And just like in classes, if you define an explicit constructor, the compiler will not insert a default constructor, so this will not compile:

public enum King {
    ELVIS, // error, default constructor is not defined
    MICHAEL_JACKSON(true)
    ;
    private boolean kingOfPop;
    King(boolean kingOfPop){this.kingOfPop = kingOfPop;}
}

This is a pretty good reference on enums that also explains the constructor issues.

Fields and Accessors

Enums are constants and are immutable as such. They can however define fields, that can have state. This is an awful practice, because developers will expect enums and their associated values to be constants, but you can still define a non-final field in an enum with getters and setters.

This is legal java code:

public enum Color {
    RED("FF0000"),
    GREEN("00FF00"),
    BLUE("0000FF");
    private String code;
    public String getCode(){return code;}
    public void setCode(String code){this.code = code;}
    private Color(String code){this.code = code;}
}

But it enables evil code like this:

String oldBlue = Color.BLUE.getCode();
Color.BLUE.setCode(Color.RED.getCode());
Color.RED.setCode(oldBlue);

So in 99.99 % of cases: if you have fields in your enums, you should make them final and provide getters only. If the fields are not immutable themselves, provide defensive copies:

public enum Band {
    THE_BEATLES("John","Paul","George","Ringo");
    private final List<String> members;
    public List<String> getMembers(){
        // defensive copy, because the original list is mutable
        return new ArrayList<String>(members);
    }
    private Band(String... members){
        this.members=Arrays.asList(members);
    }
}

Solution

In your case it's very simple: you just need a single field of type string (immutable), so initializing it in the constructor and providing a getter is perfectly ok:

public enum Checker {

    EMPTY ("Empty"),
    RED ("Red"),
    YELLOW ("Yellow");

    private final String value;

    private Checker(final String value) {
        this.value = value;
    }

    public String getValue() { return value; }
}
Andrii Abramov
  • 10,019
  • 9
  • 74
  • 96
Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
  • 1
    @Joachim: With no setter, I don't see how it makes any difference. – Michael Myers Jun 16 '10 at 14:37
  • @Joachim: exactly, I added that while you added the comment :-) – Sean Patrick Floyd Jun 16 '10 at 14:37
  • @mmyers I think there is some compiler optimization magic going on in final fields – Sean Patrick Floyd Jun 16 '10 at 14:38
  • @Yishai: I'm still not following. Granted it's been a while since I dealt with Java, let alone threading, but I don't see how merely marking a field final affects thread safety when there is no way to set it other than reflection anyway. – Michael Myers Jun 16 '10 at 15:04
  • 2
    @mmyers Strange but true. It's in the JLS. The runtime is allowed to make optimisations that it wouldn't otherwise just because of the `final`. But a better reason for adding `final` is that someone might want to read the code. – Tom Hawtin - tackline Jun 16 '10 at 15:20
  • @Tom: Ok, thanks. As I said, it's been a while. You are referring to [§17.5](http://java.sun.com/docs/books/jls/third_edition/html/memory.html#17.5)? – Michael Myers Jun 16 '10 at 15:39
  • @mmyers Yes. "In particular, compilers have a great deal of freedom to move reads of final fields across synchronization barriers and calls to arbitrary or unknown methods. Correspondingly, compilers are allowed to keep the value of a final field cached in a register and not reload it from memory in situations where a non-final field would have to be reloaded." Remember there are meta-linguistic ways of altering fields which do not necessarily invalidate the language spec. – Tom Hawtin - tackline Jun 16 '10 at 15:55
  • 3
    @mmyers: apart from what Tom said, it's also an important way to document intent. There's no reason that field should ever change, it should always be initialized once the constructor finishes and setting it should not be allowed. Why not document that with a keyword that expresses exactly that. – Joachim Sauer Jun 16 '10 at 17:32
  • Can I have more than one description? – Roy Lee Apr 21 '13 at 02:09
  • @Roylee what do you mean by a "description"? – Sean Patrick Floyd Apr 22 '13 at 07:48
  • @SeanPatrickFloyd "Empty", "Red", "Yellow" as per the answer above :) – Roy Lee Apr 22 '13 at 07:49
4

If the pattern holds, this works as well and eliminates the repetition:

public enum Checker {
    EMPTY,
    RED,
    YELLOW;


    public String getDescription(){
        String name = name();
        return ""+Character.toUpperCase(name.charAt(0))
                 +name.substring(1).toLowerCase();
    }

}
Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
  • 7
    Why is this better? You're reallocating all those strings every time, and I wouldn't particularly say this is more readable. – danben Jun 16 '10 at 14:47
  • I would cache that description in a value field, but it is a nice way of avoiding the duplication in the declaration of each enum. – Yishai Jun 16 '10 at 14:50
  • 2
    It avoids the repetition of the name (DRY principle) and requires no constructor or field (and thus less memory). If reallocation is a problem and using the memory is not, one could cache them in a field or an EnumMap. Most likely it does not matter either way and the shorter solution is better. – Michael Borgwardt Jun 16 '10 at 14:53
  • 2
    Actually, there could be two constructors, one with a value that stores the value directly and one empty constructor that generates the value using your getDescription method. I would however write them to a field anyway – Sean Patrick Floyd Jun 16 '10 at 14:56
  • this is cool too, glad to know of it. But pattern may change later. thank you for your help. – iTEgg Jun 16 '10 at 15:25
  • 2
    Clearly you can take DRY way too far! It's not even certain that the enum name and "related" `String` will always be related in the way they naively appear. You might want `FUSCHIA("Fuchsia"), WTF("Dusty Lavendar")`. – Tom Hawtin - tackline Jun 16 '10 at 15:28
  • 2
    @Tom exactly, that's why I proposed the two constructor scenario – Sean Patrick Floyd Jun 16 '10 at 15:35
  • @Tom: that's what I wrote "if the pattern holds". As for things possibly not being in the future like they "naively appear" now - YAGNI. – Michael Borgwardt Jun 16 '10 at 16:53