63

我试图了解 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;
    }
}
4

5 回答 5

58

差异:

  1. 枚举扩展java.lang.Enum并获得了它所有的优点
    1. 通过正确的序列化实现自动单例行为
    2. 枚举值的自动人类可读.toString方法,无需复制您的枚举名称
    3. .name.ordinal特殊用途的方法
    4. 可用于基于位集的高性能EnumSetEnumMap
  2. 枚举由语言特别处理:
    1. 枚举使用一种特殊的语法来简化实例的创建,而无需编写数十个public static final字段
    2. 枚举可以在switch语句中使用
    3. 枚举不能在枚举列表之外实例化,除非使用反射
    4. 枚举不能扩展到枚举列表之外
  3. Java 自动将额外的东西编译到枚举中:
    1. public static (Enum)[] values();
    2. public static (Enum) valueOf(java.lang.String);
    3. private static final (Enum)[] $VALUES;values()返回这个的克隆)

其中大部分都可以用适当设计的类来模拟,但Enum只是让创建一个具有这组特别理想属性的类变得非常容易。

于 2013-03-31T21:04:04.747 回答
10

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

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

于 2013-03-31T20:58:16.063 回答
7

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

new Direction(2, 2)

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

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

于 2013-03-31T20:40:46.833 回答
5

正如人们指出的那样,您输了values()valueOf()并且ordinal()。您可以使用 aMap和 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;
    }
}

如你看到的; 代码很快变得非常笨拙。

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

于 2013-03-31T21:03:34.240 回答
0

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

  1. enum对象有这样的方法name()ordinal()
  2. enum对象有特殊toString()hashCode(),equals()compareTo()实现
  3. enum对象适合switch操作员。

上述所有内容均不适用于您的Direction课程版本。这就是“意义”的区别。

于 2013-03-31T20:52:17.363 回答