Java枚举和具有私有构造函数的类之间有什么区别?

时间:2013-03-31 20:33:26

标签: java enums enumeration

我试图理解Java枚举是如何工作的,我得出的结论是它与普通的Java类非常相似,它的构造函数被声明为私有。

我刚刚得出这个结论,并不是基于多少思考,但我想知道我是否遗漏了任何东西。

下面是一个简单的Java枚举和一个等效的Java类的实现。

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;
    }
}

上面和下面的代码之间有什么区别?

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;
    }
}

5 个答案:

答案 0 :(得分:57)

的差异:

  1. 枚举扩展java.lang.Enum并获得所有nice features
    1. 通过正确序列化自动单例行为
    2. 枚举值上的人工可读.toString方法,无需复制枚举名称
    3. .name.ordinal专用方法
    4. 适用于基于高性能bitset的EnumSetEnumMap
  2. Enums受特殊语言处理:
    1. 枚举使用特殊语法,可以简化实例创建,而无需编写数十个public static final字段
    2. 枚举可用于switch语句
    3. 除了使用反射
    4. 之外,无法在枚举列表外实例化枚举
    5. 枚举不能在枚举列表之外扩展
  3. Java自动将额外的东西编译到枚举中:
    1. public static (Enum)[] values();
    2. public static (Enum) valueOf(java.lang.String);
    3. private static final (Enum)[] $VALUES;values()返回此克隆)
  4. 大多数这些都可以使用适当设计的类进行模拟,但Enum只是使用这组特别理想的属性创建一个类非常容易。

答案 1 :(得分:9)

回答这个问题:基本上,这两种方法没有区别。但是,enum构造为您提供了一些其他支持方法,如values()valueOf()等,您必须使用class-with-private-constructor方法自行编写。

但是,我喜欢Java枚举大多与Java中的任何其他类一样,它们可以有字段,行为等。但是对于我来说,将枚举与普通类分开的是枚举是类/类型的想法实例/成员是预先确定的。与可以从中创建任意数量实例的常规类不同,枚举仅限于创建已知实例。是的,正如您所说明的那样,您也可以使用私有构造函数的类来执行此操作,但枚举只是使这更直观。

答案 2 :(得分:6)

看一下this blogpage,它描述了如何将Java enum编译成字节码。与第二个代码示例相比,您会看到一个小的添加,这是一个名为Direction的{​​{1}}个对象数组。此数组包含枚举的所有可能值,因此您将无法执行

VALUES

(例如使用反射),然后将其用作有效的new Direction(2, 2) 值。

另外,正如@Eng.Fouad正确解释的那样,您没有Directionvalues()valueOf()

答案 3 :(得分:5)

正如人们指出的那样,你失去了values()valueOf()ordinal()。您可以使用MapList的组合轻松复制此行为。

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;
    }
}

正如你所看到的;代码变得非常笨重。

我不确定是否有办法用这个类复制switch语句;所以你会失去它。

答案 4 :(得分:0)

主要区别在于每个enum类隐式扩展Enum<E extends Enum<E>>类。这导致:

  1. enum个对象包含name()ordinal()
  2. 等方法
  3. enum个对象具有特殊toString()hashCode()equals()compareTo()实施
  4. enum个对象适合switch运算符。
  5. 上述所有内容均不适用于您的Direction课程版本。这是“意义”的区别。