Java枚举和带有私有构造函数的类之间有什么区别?
我试图理解Java枚举是如何工作的,而且我得出的结论与一般的Java类非常类似,它的构造函数声明为private。
我刚刚得出这个结论,并不是基于很多的想法,但我想知道我是否错过任何东西。
所以下面是一个简单的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; } }
区别:
- 枚举扩展
java.lang.Enum
并获得其所有好的function :- 通过正确的序列化自动单例行为
- 在枚举值上自动生成可读的
.toString
方法,而不需要复制枚举名 -
.name
和.ordinal
特殊用途的方法 - 可用于高性能的基于位集的
EnumSet
和EnumMap
类
- 枚举被特定的语言处理:
- 枚举使用特殊的语法来简化实例创build,而不用编写数十个
public static final
字段 - 枚举可以用在
switch
语句中 - 枚举不能在枚举列表之外实例化,除了使用reflection
- 枚举不能在枚举列表之外扩展
- 枚举使用特殊的语法来简化实例创build,而不用编写数十个
- Java自动编译额外的东西到枚举中:
-
public static (Enum)[] values();
-
public static (Enum) valueOf(java.lang.String);
-
private static final (Enum)[] $VALUES;
(values()
返回这个的一个副本)
-
其中大部分可以用适当devise的类来模拟,但Enum
只是使用这组特别需要的属性创build一个类非常容易。
看看这个博客页面 ,它描述了如何将Java enum
编译成字节码。 您将看到,与您的第二个代码示例(这是一个名为VALUES
的Direction
对象数组)相比,它有一个小的增加。 这个数组包含了你的枚举的所有可能的值,所以你将无法做到
new Direction(2, 2)
(例如使用reflection),然后将其用作有效的Direction
值。
另外,正如@ Eng.Fouad正确解释的,你没有values()
, valueOf()
和ordinal()
。
为了回答这个问题:实质上,这两种方法没有区别。 但是,enum构造提供了一些额外的支持方法,比如values()
, valueOf()
等等,你必须用class-private-constructor方法自己编写。
但是,我喜欢Java枚举类似于Java中的其他任何类,他们可以有字段,行为等等。但是对于枚举类和普通类的区别在于枚举类是其实例/成员是预定的。 与通常可以创build任意数量实例的类不同,枚举只能将创build限制为已知实例。 是的,正如你所说的,你也可以使用私有构造函数来完成这个工作,但枚举只是让这个更直观。
正如人们指出的,你失去了values()
, valueOf()
和ordinal()
。 您可以使用Map
和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
语句; 所以你会失去的。
主要区别在于每个enum
类都隐式扩展了Enum<E extends Enum<E>>
类。 这导致:
-
enum
对象具有name()
和ordinal()
-
enum
对象具有特殊的toString()
,hashCode()
,equals()
和compareTo()
实现 -
enum
对象适合switch
操作员。
上面提到的所有内容都不适用于您的Direction
类的版本。 这是“意义”的区别。