Java enum和具有私有构造函数的类之间的区别是什么?(复制)

时间:2022-04-27 17:18:26

This question already has an answer here:

这个问题已经有了答案:

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.

我试图理解Java enum是如何工作的,我得出的结论是,它非常类似于声明为private的普通Java类。

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.

因此,下面是一个简单的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;
    }
}

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

5 个解决方案

#1


48  

Differences:

差异:

  1. Enums extend java.lang.Enum and gain all of its nice features:
    1. Automatic singleton behaviour through correct serialization
    2. 通过正确的序列化自动单例行为。
    3. Automatic human-readable .toString method on enum values without the need to duplicate your enum names
    4. 枚举值上的自动可读的.toString方法,不需要重复您的枚举名称
    5. .name and .ordinal special-purpose methods
    6. .name和.ordinal专用方法
    7. Usable in high-performance bitset-based EnumSet and EnumMap classes
    8. 可用于基于高性能位集的枚举集和枚举映射类
  2. 枚举扩展. lang。枚举并获得它所有的优秀特性:通过正确的串行化自动人可读的枚举值上的.toString方法自动独立行为,不需要重复枚举名.name和.ordinal特殊用途的方法,这些方法可用于基于位集的高性能枚举集和枚举映射类
  3. 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. 枚举使用一种特殊的语法来简化实例创建,而不需要编写几十个公共静态最终字段
    3. Enums can be used in switch statements
    4. 枚举可以在switch语句中使用。
    5. Enums cannot be instantiated outside the enumeration list except by using reflection
    6. 枚举不能在枚举列表之外实例化,除非使用反射
    7. Enums cannot be extended outside the enumeration list
    8. 枚举不能扩展到枚举列表之外
  4. 枚举是由语言治疗的特殊:枚举使用一个特殊的语法简化实例创建没有写几十个公共静态final字段的枚举可以用于switch语句枚举不能实例化以外的枚举列表除了使用反射枚举不能外扩展枚举列表
  5. Java automatically compiles extra stuff into enums:
    1. public static (Enum)[] values();
    2. 公共静态(枚举)[]值();
    3. public static (Enum) valueOf(java.lang.String);
    4. 公共静态(枚举)返回对象的值(以);
    5. private static final (Enum)[] $VALUES; (values() returns a clone of this)
    6. 私有静态final (Enum)[] $值;(values()返回这个的克隆)
  6. Java自动将额外的内容编译成枚举:public static (Enum)[] values();公共静态(枚举)返回对象的值(以);私有静态final (Enum)[] $值;(values()返回这个的克隆)

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.

大多数这些都可以通过适当设计的类来模拟,但是Enum使得创建具有这种特别理想属性的类变得非常容易。

#2


7  

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.

回答这个问题:本质上,这两种方法没有区别。然而,enum构造为您提供了一些额外的支持方法,比如values()、valueOf()等,您必须使用private构造函数方法自己编写这些方法。

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.

但是,我喜欢Java枚举和Java中的其他类一样,它们可以有字段、行为等等。但是对我来说,将枚举与普通类分开的是,枚举是类/类型,其实例/成员是预先确定的。不像通常的类,您可以创建任意数量的实例,枚举只将创建限制为已知的实例。是的,正如您所演示的,您也可以使用私有构造函数来实现这一点,但是enum只会使这一点更加直观。

#3


6  

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

看看这个博客页面,它描述了如何将Java枚举编译成字节码。与第二个代码示例相比,您将看到一个小的添加,该示例是一个名为VALUES的方向对象数组。这个数组包含枚举的所有可能值,因此您将无法执行。

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().

另外,@Eng。Fouad正确地解释了,你没有value (), valueOf()和ordinal()。

#4


4  

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.

正如人们指出的那样,您丢失了values()、valueOf()和ordinal()。使用地图和列表的组合可以很容易地复制这种行为。

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.

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

#5


0  

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

主要的区别是每个enum类隐式扩展enum >类。这将导致: 扩展enum>

  1. enum objects have such methods as name() and ordinal()
  2. enum对象具有名称()和序号()等方法
  3. enum objects have special toString(), hashCode(), equals() and compareTo() implementations
  4. enum对象具有特殊的toString()、hashCode()、equals()和compareTo()实现
  5. enum objects are suitable for switch operator.
  6. enum对象适用于开关操作符。

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

以上所提到的都不适用于您的方向类版本。这就是“意义”的区别。

#1


48  

Differences:

差异:

  1. Enums extend java.lang.Enum and gain all of its nice features:
    1. Automatic singleton behaviour through correct serialization
    2. 通过正确的序列化自动单例行为。
    3. Automatic human-readable .toString method on enum values without the need to duplicate your enum names
    4. 枚举值上的自动可读的.toString方法,不需要重复您的枚举名称
    5. .name and .ordinal special-purpose methods
    6. .name和.ordinal专用方法
    7. Usable in high-performance bitset-based EnumSet and EnumMap classes
    8. 可用于基于高性能位集的枚举集和枚举映射类
  2. 枚举扩展. lang。枚举并获得它所有的优秀特性:通过正确的串行化自动人可读的枚举值上的.toString方法自动独立行为,不需要重复枚举名.name和.ordinal特殊用途的方法,这些方法可用于基于位集的高性能枚举集和枚举映射类
  3. 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. 枚举使用一种特殊的语法来简化实例创建,而不需要编写几十个公共静态最终字段
    3. Enums can be used in switch statements
    4. 枚举可以在switch语句中使用。
    5. Enums cannot be instantiated outside the enumeration list except by using reflection
    6. 枚举不能在枚举列表之外实例化,除非使用反射
    7. Enums cannot be extended outside the enumeration list
    8. 枚举不能扩展到枚举列表之外
  4. 枚举是由语言治疗的特殊:枚举使用一个特殊的语法简化实例创建没有写几十个公共静态final字段的枚举可以用于switch语句枚举不能实例化以外的枚举列表除了使用反射枚举不能外扩展枚举列表
  5. Java automatically compiles extra stuff into enums:
    1. public static (Enum)[] values();
    2. 公共静态(枚举)[]值();
    3. public static (Enum) valueOf(java.lang.String);
    4. 公共静态(枚举)返回对象的值(以);
    5. private static final (Enum)[] $VALUES; (values() returns a clone of this)
    6. 私有静态final (Enum)[] $值;(values()返回这个的克隆)
  6. Java自动将额外的内容编译成枚举:public static (Enum)[] values();公共静态(枚举)返回对象的值(以);私有静态final (Enum)[] $值;(values()返回这个的克隆)

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.

大多数这些都可以通过适当设计的类来模拟,但是Enum使得创建具有这种特别理想属性的类变得非常容易。

#2


7  

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.

回答这个问题:本质上,这两种方法没有区别。然而,enum构造为您提供了一些额外的支持方法,比如values()、valueOf()等,您必须使用private构造函数方法自己编写这些方法。

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.

但是,我喜欢Java枚举和Java中的其他类一样,它们可以有字段、行为等等。但是对我来说,将枚举与普通类分开的是,枚举是类/类型,其实例/成员是预先确定的。不像通常的类,您可以创建任意数量的实例,枚举只将创建限制为已知的实例。是的,正如您所演示的,您也可以使用私有构造函数来实现这一点,但是enum只会使这一点更加直观。

#3


6  

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

看看这个博客页面,它描述了如何将Java枚举编译成字节码。与第二个代码示例相比,您将看到一个小的添加,该示例是一个名为VALUES的方向对象数组。这个数组包含枚举的所有可能值,因此您将无法执行。

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().

另外,@Eng。Fouad正确地解释了,你没有value (), valueOf()和ordinal()。

#4


4  

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.

正如人们指出的那样,您丢失了values()、valueOf()和ordinal()。使用地图和列表的组合可以很容易地复制这种行为。

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.

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

#5


0  

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

主要的区别是每个enum类隐式扩展enum >类。这将导致: 扩展enum>

  1. enum objects have such methods as name() and ordinal()
  2. enum对象具有名称()和序号()等方法
  3. enum objects have special toString(), hashCode(), equals() and compareTo() implementations
  4. enum对象具有特殊的toString()、hashCode()、equals()和compareTo()实现
  5. enum objects are suitable for switch operator.
  6. enum对象适用于开关操作符。

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

以上所提到的都不适用于您的方向类版本。这就是“意义”的区别。