64

I was trying to understand how Java enum really works and I have come to the conclusion that it is very similar to a normal Java class that has its constructor declared private.

I have just come to this conclusion and it isn't based on much thinking, but Id like to know whether I miss anything.

So below is an implementation of a simple Java enum and an equivalent Java class.

public enum Direction {
    ENUM_UP(0, -1),
    ENUM_DOWN(0, 1),
    ENUM_RIGHT(1, 0),
    ENUM_LEFT(-1, 0);


    private int x;
    private int y;

    private Direction(int x, int y){
        this.x = x;
        this.y = y;
    }
    public int getEnumX(){
        return x;
    }
    public int getEnumY(){
        return y;
    }
}

What is the difference in meaning between the code above and below?

public class Direction{
    public static final Direction UP = new Direction(0, -1) ;
    public static final Direction DOWN = new Direction(0, 1) ;
    public static final Direction LEFT = new Direction(-1, 0) ;
    public static final Direction RIGHT = new Direction(1, 0) ;


    private int x ;
    private int y ;

    private Direction(int x, int y){
        this.x = x ;
        this.y = y ;
    }
    public int getX(){
        return x;
    }
    public int getY(){
        return y;
    }
}
urig
  • 16,016
  • 26
  • 115
  • 184
user9349193413
  • 1,203
  • 2
  • 14
  • 26
  • 7
    Good question, they are indeed similar in many ways :) With plenty of differences too I'm sure good answers here will outline. – Benjamin Gruenbaum Mar 31 '13 at 20:35
  • 4
    At least three differences I can think of: [1] you won't be able to switch over the latter implementation (ie. `Direction d; switch(d)`), [2] the `Direction.toString()` implementations will be different, [3] The enum method allows you to get all instances of the "class" via `.values()` whereas the class method does not. – jedwards Mar 31 '13 at 20:39
  • 3
    You will lose `values()`, `valueOf()` and `ordinal()`. – Eng.Fouad Mar 31 '13 at 20:40
  • @jedwards It's not possible to do `switch` on a normal object in Java? Wow. – thejh Mar 31 '13 at 20:44
  • 1
    Enum's are an enhancement build *into* the language. To express something, it is shorter and clearer. You may take Scala as an example: Scala build into the language a lot of things that you have to express with lot of boilerplate code in pure Java. Singletons for example. Java is very (extreme) conservative in that aspect. – PeterMmm Mar 31 '13 at 20:49
  • @thejh you think that it is possible? Show us how. – Andremoniy Mar 31 '13 at 20:53
  • @Andremoniy Nah, you're right. I just think it's weird. – thejh Mar 31 '13 at 21:03
  • 3
    Voted to reopen. If anything, the [other question](http://stackoverflow.com/questions/9969690/whats-the-advantage-of-a-java-enum-versus-a-class-with-public-static-final-fiel) should be closed as duplicate of this, since this is clearer and has a way better answer. – Jonik Apr 02 '13 at 18:28

5 Answers5

59

Differences:

  1. Enums extend java.lang.Enum and gain all of its nice features:
    1. Automatic singleton behaviour through correct serialization
    2. Automatic human-readable .toString method on enum values without the need to duplicate your enum names
    3. .name and .ordinal special-purpose methods
    4. Usable in high-performance bitset-based EnumSet and EnumMap classes
  2. Enums are treated by the language specially:
    1. Enums use a special syntax which simplifies instance creation without writing dozens of public static final fields
    2. Enums can be used in switch statements
    3. Enums cannot be instantiated outside the enumeration list except by using reflection
    4. Enums cannot be extended outside the enumeration list
  3. Java automatically compiles extra stuff into enums:
    1. public static (Enum)[] values();
    2. public static (Enum) valueOf(java.lang.String);
    3. private static final (Enum)[] $VALUES; (values() returns a clone of this)

Most of these can be emulated with a suitably designed class, but Enum just makes it really easy to create a class with this set of particularly desirable properties.

nneonneo
  • 171,345
  • 36
  • 312
  • 383
  • how does the singleton work? for each value it has a singleton ? what if you create an enum that has setters and getters? would using setters on a single enum variable affect other variables with the same value? – android developer Jan 25 '14 at 23:58
  • Each Enum value is a singleton, so yes, using a setter on one will affect all variables referencing that value. You are therefore strongly advised to never have mutable state in your Enums. – nneonneo Feb 26 '14 at 01:32
  • 1
    I see. seems logical. thanks. – android developer Feb 26 '14 at 07:47
10

To answer the question: essentially, there's no difference between the two approaches. However, enum construct provides you with some additional supporting methods like values(), valueOf(), etc. which you'd have to write on your own with the class-with-private-constructor approach.

But yeah, I like how Java enums are mostly just like any other classes in Java, they can have fields, behaviors, etc. But to me what separates enums from the plain classes is the idea that enums are classes/types whose instances/members are predetermined. Unlike usual classes where you can create any number of instances from, enums only limit creation to known instances. Yes, as you've illustrated, you can also do this with classes with private constructors, but enums just make this more intuitive.

Psycho Punch
  • 6,418
  • 9
  • 53
  • 86
7

Take a look at this blogpage, it describes how Java enums are compiled into bytecode. You'll see that there's a small addition compared to your second code sample, which is an array of Direction objects called VALUES. This array holds all possible values for your enum, so you won't be able to do

new Direction(2, 2)

(for example using reflection) and then use that as a valid Direction value.

Plus, as @Eng.Fouad correctly explains, you don't have values(), valueOf() and ordinal().

mthmulders
  • 9,483
  • 4
  • 37
  • 54
  • 1
    I know you can *hack* the private constructor of the `Direction` class using reflection and create new instances of it, but I'm not sure if you can do the same with enums. – Luiggi Mendoza Mar 31 '13 at 20:41
  • 1
    According to the way they're compiled to bytecode, you might be able to. I never tried myself, interesting experiment though... – mthmulders Mar 31 '13 at 20:42
6

As people have pointed out you lose values(), valueOf() and ordinal(). You can replicate this behaviour fairly easily using a combination of a Map and a List.

public class Direction {

    public static final Direction UP = build("UP", 0, -1);
    public static final Direction DOWN = build("DOWN", 0, 1);
    public static final Direction LEFT = build("LEFT", -1, 0);
    public static final Direction RIGHT = build("RIGHT", 1, 0);
    private static final Map<String, Direction> VALUES_MAP = new LinkedHashMap<>();
    private static final List<Direction> VALUES_LIST = new ArrayList<>();
    private final int x;
    private final int y;
    private final String name;

    public Direction(int x, int y, String name) {
        this.x = x;
        this.y = y;
        this.name = name;
    }

    private static Direction build(final String name, final int x, final int y) {
        final Direction direction = new Direction(x, y, name);
        VALUES_MAP.put(name, direction);
        VALUES_LIST.add(direction);
        return direction;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public static Direction[] values() {
        return VALUES_LIST.toArray(new Direction[VALUES_LIST.size()]);
    }

    public static Direction valueOf(final String direction) {
        if (direction == null) {
            throw new NullPointerException();
        }
        final Direction dir = VALUES_MAP.get(direction);
        if (dir == null) {
            throw new IllegalArgumentException();
        }
        return dir;
    }

    public int ordinal() {
        return VALUES_LIST.indexOf(this);
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 29 * hash + name.hashCode();
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Direction other = (Direction) obj;
        return name.equals(other.name);
    }

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

As you can see; the code becomes very clunky very quickly.

I'm not sure if there is a way for replicate a switch statement with this class; so you will lose that.

Boris the Spider
  • 59,842
  • 6
  • 106
  • 166
0

The main difference is the each enum class implicitly extends Enum<E extends Enum<E>> class. This leads to that:

  1. enum objects have such methods as name() and ordinal()
  2. enum objects have special toString(), hashCode(), equals() and compareTo() implementations
  3. enum objects are suitable for switch operator.

All mentioned above is not applicable for your version of Direction class. This is the "meaning" difference.

Andremoniy
  • 34,031
  • 20
  • 135
  • 241