Java中的“enum”有什么用?

67

我研究了一下这个“枚举”类型,感觉它有点像是一个夸大的数组/ArrayList/List。那么它到底有什么用处呢?


9
它在你看来有哪些类似数组的地方?它不能存储任何东西,也无法取出其中的内容,它里面也没有任何东西...... 基本上它缺乏人们通常与数组相关的所有特征。 - sepp2k
1
链接到我另一个答案,有些人发现它很有用。我知道它在比较Java枚举和C++枚举的线程中,但我试图在那里描述Java枚举属性。 - Boris Strandjev
4
@sepp2k:啊,我明白了:源代码中的定义有点像列表字面量... - Michael Borgwardt
枚举不是数组,但如果您的枚举是MyEnum,则MyEnum.values()是枚举中所有常量的数组。 - Ram Mandal
8个回答

71

枚举类型是一种固定常数数量的类型,至少可用于两个方面:

常量

public enum Month {
    JANUARY, FEBRUARY, ...
}

这比创建一堆整数常量好多了。

创建单例

public enum Singleton {
    INSTANCE

   // init
};

枚举类型非常有趣,你可以做很多有意思的事情,看看这里

同时也请查看官方文档


这两件事情基本上是一样的。单例也是一个“常量”,不是吗? - aioobe
1
@aioobe 它展示了枚举类型作为单例模式的用法。第一个例子(常量)是一个固定的常量列表。而这个例子则更像一个普通类,但Java本身确保只创建一次该类。 - user219882
啊,但你同样可以将两个参数反过来:单例是一个固定的常量列表,而Month枚举只是一个普通类,除了Java确保该类仅被实例化12次。你说“至少可以用于两件事”,我认为这两件事是相同的。:-) 你同意吗? - aioobe
@aioobe 不是的。Singleton 不是一个固定的常量列表,而是指特定对象仅被实例化一次。那完全是不同的情况。我想你在这里误解了重点。 - user219882
2
@Tomas 我不会说 Singleton 的使用是“完全不同”的。程序员的意图可能不同,因为 Singleton 在应用程序中通常扮演更重要的角色,而多值枚举通常只是一些小的具体细节。也许这就是你在评论中想表达的。但从技术上讲,Singleton 枚举只是一个“固定的常量列表”,它恰好只有一个元素。正如 aioobe 所问的那样,这个答案中的两个示例,在技术上讲,其实是完全相同的东西;一个是包含 12 个值的枚举,另一个是包含 1 个值的枚举。 - Basil Bourque

71

如果一个类应该具有固定的枚举实例数量,您应该使用enum而不是class

例如:

  • DayOfWeek  = 7个实例 → enum
  • CardSuit    = 4个实例 → enum
  • Singleton  = 1个实例   → enum

  • Product      = 可变数量的实例 → class
  • User            = 可变数量的实例 → class
  • Date            = 可变数量的实例 → class

还可用于应用程序中的语言切换等功能,例如菜单名称等。 - keyser
没错,当选项在编译时已知 :) - aioobe
是的,当然。只是添加一个例子 :p - keyser
当类的实例具有非常相似或可注入的行为模式时,在枚举自身的方法中进行 switch-case 类情况可能是一种代码异味。 - Kenogu Labz

46

枚举类型是一种数据类型,它让你以更可读和可靠的方式描述类型的每个成员。

这里有一个简单的例子来解释:

假设你正在编写一个与季节有关的方法:

int枚举模式

首先,你声明了一些int静态常量来代表每个季节。

public static final int SPRING = 0;
public static final int SUMMER = 1;
public static final int FALL = 2;
public static final int WINTER = 2;

然后,你声明了一个方法将季节的名称打印到控制台中。

public void printSeason(int seasonCode) {
    String name = "";
    if (seasonCode == SPRING) {
        name = "Spring";
    }
    else if (seasonCode == SUMMER) {
        name = "Summer";
    }
    else if (seasonCode == FALL) {
        name = "Fall";
    }
    else if (seasonCode == WINTER) {
        name = "Winter";
    }
    System.out.println("It is " + name + " now!");
}

那么,之后你可以像这样打印一个季节名称。

printSeason(SPRING);
printSeason(WINTER);

这是一种相当常见(但糟糕)的方式,在类的不同成员类型中进行不同的处理。然而,由于这些代码涉及整数,因此您可以毫无问题地像这样调用该方法。

printSeason(0);
printSeason(1);

甚至可以像这样

printSeason(x - y);
printSeason(10000);
编译器不会抱怨这些方法调用是有效的,你的 printSeason 方法仍然可以工作。但是这里有些不对劲。10000 表示什么季节代码?如果 x-y 的结果为负数怎么办?当你的方法接收到一个没有意义且不应该存在的输入时,你的程序对此一无所知。你可以通过添加额外的检查来解决这个问题。
...
else if (seasonCode == WINTER) {
    name = "Winter";
}
else {
    throw new IllegalArgumentException();
}
System.out.println(name);
现在当季节代码无效时,程序将抛出一个RunTimeException。然而,您仍然需要决定如何处理异常。
顺便说一下,我相信您已经注意到了FALLWINTER的代码都是2,对吗?
现在你应该有了想法。这种模式很容易出错。它让您在各处编写条件检查。如果您正在制作一个游戏,并且想要在虚构的世界中添加一个额外的季节,这种模式将使您遍历所有按季节执行任务的方法,而在大多数情况下您会忘记其中一些。
您可能认为类继承是这种情况下的一个好主意。但我们只需要其中的一些,不需要更多。
这就是enum发挥作用的时候。
使用枚举类型
在Java中,enum类型是通过公共静态final字段导出每个枚举常量的类。
在这里,您可以声明四个枚举常量:SPRING, SUMMER, FALL, WINTER。每个都有自己的name
public enum Season {
    SPRING("Spring"), SUMMER("Summer"), FALL("Fall"), WINTER("Winter");

    private String name;

    Season(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

现在回到方法。

public void printSeason(Season season) {
    System.out.println("It is " + season.getName() + " now!");
}

你现在可以使用Season作为输入,而不是使用int。你可以告诉Season返回它的名称,而不是进行条件检查。

现在使用此方法的方式如下:

printSeason(Season.SPRING);
printSeason(Season.WINTER);
printSeason(Season.WHATEVER); <-- compile error

只要程序编译通过,当您使用不正确的输入时,将会得到一个编译时错误,并且保证会获得一个非空的单例引用Season

当我们需要添加另一个季节时,只需在Season中添加另一个常量即可,无需进行其他操作。

public enum Season {
    SPRING("Spring"), SUMMER("Summer"), FALL("Fall"), WINTER("Winter"), 
    MYSEASON("My Season");

...
无论何时你需要一个固定的常量集合,enum 可能是一个很好的选择(但不总是)。它是一种更易读、更可靠和更强大的解决方案。

28

枚举类型是一种类型,其字段由固定的常量集合组成。常见的例子包括罗盘方向(NORTH、SOUTH、EAST 和 WEST 值)和一周中的天。

public enum Day {
    SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
    THURSDAY, FRIDAY, SATURDAY 
}

每当您需要表示一组固定的常量时,您应该使用枚举类型。这包括自然枚举类型,例如我们太阳系中的行星和数据集,在编译时您知道所有可能的值 - 例如菜单上的选项、命令行标志等。

下面是一些代码,展示了如何使用上面定义的Day枚举类型:

public class EnumTest {
    Day day;

    public EnumTest(Day day) {
        this.day = day;
    }

    public void tellItLikeItIs() {
        switch (day) {
            case MONDAY:
                System.out.println("Mondays are bad.");
                break;

            case FRIDAY:
                System.out.println("Fridays are better.");
                break;

            case SATURDAY: case SUNDAY:
                System.out.println("Weekends are best.");
                break;

            default:
                System.out.println("Midweek days are so-so.");
                break;
        }
    }

    public static void main(String[] args) {
        EnumTest firstDay = new EnumTest(Day.MONDAY);
        firstDay.tellItLikeItIs();
        EnumTest thirdDay = new EnumTest(Day.WEDNESDAY);
        thirdDay.tellItLikeItIs();
        EnumTest fifthDay = new EnumTest(Day.FRIDAY);
        fifthDay.tellItLikeItIs();
        EnumTest sixthDay = new EnumTest(Day.SATURDAY);
        sixthDay.tellItLikeItIs();
        EnumTest seventhDay = new EnumTest(Day.SUNDAY);
        seventhDay.tellItLikeItIs();
    }
}

输出结果是:

周一糟糕。
中间的几天还好。
周五更好。
周末最棒。
周末最棒。

Java编程语言枚举类型比其他语言中的枚举类型更加强大。 枚举声明定义了一个类(称为枚举类型)。 枚举类体可以包括方法和其他字段。 编译器在创建枚举时自动添加一些特殊方法。例如,它们具有静态值方法,该方法返回按声明顺序包含枚举所有值的数组。此方法通常与for-each结构结合使用,以便遍历枚举类型的值。例如,下面来自Planet类示例的代码遍历了太阳系中的所有行星。

for (Planet p : Planet.values()) {
    System.out.printf("Your weight on %s is %f%n",
                      p, p.surfaceWeight(mass));
}

除了它的属性和构造函数,Planet 还有一些方法可以让您检索每个行星上物体的表面重力和重量。这是一个示例程序,它以地球上的重量(以任何单位)为基础,计算并打印出所有行星上的重量(以相同的单位):

public enum Planet {
    MERCURY (3.303e+23, 2.4397e6),
    VENUS   (4.869e+24, 6.0518e6),
    EARTH   (5.976e+24, 6.37814e6),
    MARS    (6.421e+23, 3.3972e6),
    JUPITER (1.9e+27,   7.1492e7),
    SATURN  (5.688e+26, 6.0268e7),
    URANUS  (8.686e+25, 2.5559e7),
    NEPTUNE (1.024e+26, 2.4746e7);

    private final double mass;   // in kilograms
    private final double radius; // in meters
    Planet(double mass, double radius) {
        this.mass = mass;
        this.radius = radius;
    }
    private double mass() { return mass; }
    private double radius() { return radius; }

    // universal gravitational constant  (m3 kg-1 s-2)
    public static final double G = 6.67300E-11;

    double surfaceGravity() {
        return G * mass / (radius * radius);
    }
    double surfaceWeight(double otherMass) {
        return otherMass * surfaceGravity();
    }
    public static void main(String[] args) {
        if (args.length != 1) {
            System.err.println("Usage: java Planet <earth_weight>");
            System.exit(-1);
        }
        double earthWeight = Double.parseDouble(args[0]);
        double mass = earthWeight/EARTH.surfaceGravity();
        for (Planet p : Planet.values())
           System.out.printf("Your weight on %s is %f%n",
                             p, p.surfaceWeight(mass));
    }
}
如果你在命令行中以参数175运行Planet.class,你会得到以下输出结果:

$ java Planet 175
你在水星上的体重为66.107583
你在金星上的体重为158.374842
你在地球上的体重为175.000000
你在火星上的体重为66.279007
你在木星上的体重为442.847567
你在土星上的体重为186.552719
你在天王星上的体重为158.397260
你在海王星上的体重为199.207413

来源:http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html

16
既然你直接复制官方文档而不加修改,那么至少可以注明出处。 - user219882
冷静点,伙计们,我只是专注于帮助他,没有别的!源链接现在已经附上!!! - Ashraf Bashir
1
说实话,这段文字以一个相当误导性的陈述开始。枚举可以拥有不止一组常量字段。 - aioobe
是的,你说得没错,但如果你继续阅读,你会找到更多关于枚举的描述,可以详细解释它的用法。 - Ashraf Bashir

2

枚举类型是一种安全类型,因此无法在运行时分配新值。此外,您可以在 switch 语句中使用它(就像 int 类型一样)。


1
我不理解这个答案。普通类也是类型安全的。如果你正在将其与使用整数进行比较,那么为什么要强调您可以在 switch 中使用它们?! - aioobe
在 switch 语句中使用枚举比整数更加优雅。 - Guillaume USE
你为什么这么说?(我认为使用枚举比使用整数更优雅,而且您不必牺牲switch语句。) - aioobe
1
switch(i) { ... case 11: delete(); break; ... }switch(actions) { ... case DELETE: delete(); break; ... }哪段代码最干净?枚举,不是吗? - Guillaume USE
1
你的代码中不使用命名常量吗?比如public static int DELETE = 11。如果使用了,那么switch语句看起来与枚举情况相同。 - aioobe
2
如果你在switch语句中使用枚举,那么你的做法是错误的 - 反转它,使代码在枚举中。enum.doThings(stuff)比switch(enum) { case THING1: { } case THING2: {} }更容易阅读 - 它还可以减少圈复杂度,使代码更易于阅读和测试。 - MetroidFan2002

0

枚举类型是提供一组易于记忆的名称(可带有一些有限的行为)的推荐方式

如果您需要使用多个静态整数常量(例如public static int ROLE_ADMIN = 0BLOOD_TYPE_AB = 2),则应使用枚举。

相比之下,使用枚举的主要优势是类型安全性高,在尝试使用错误值时会产生编译器类型警告/错误,并提供相关“常量”的命名空间。此外,它们在IDE中更容易使用,因为它也可以帮助代码完成。


0
Java编程语言的枚举类型enums比其他语言中的相应类型要强大得多,其他语言中的枚举类型不过是普通的整数。新的枚举声明定义了一个完整的类(称为枚举类型)。除了解决以前Java 5.0版本之前使用的int Enum模式存在的所有问题(不安全的类型、没有命名空间、脆弱性和打印值缺乏信息),enums还允许将任意方法和字段添加到枚举类型中,实现任意接口等。枚举类型提供了所有Object方法的高质量实现。它们是可比较的ComparableSerializable,而序列化形式被设计为可以承受枚举类型中的任意更改。您还可以在switch case中使用枚举类型。
阅读有关Java Enums的完整文章http://docs.oracle.com/javase/1.5.0/docs/guide/language/enums.html以了解更多细节。

1
但有时我只想要一个经过赞美的整数集合。 - Jon Trauntvein

-7

1) enum 是面向对象方法中的关键字。

2) 它用于在一行代码中编写代码,仅此而已。

     public class NAME
     {
        public static final String THUNNE = "";
        public static final String SHAATA = ""; 
        public static final String THULLU = ""; 
     }

-------This can be replaced by--------

  enum NAME{THUNNE, SHAATA, THULLU}

3) 大多数开发者不使用枚举关键字,这只是一种替代方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接