你好!今天我们就来聊一聊Java的一种特殊数据类型:
Enum
(enumeration的简称)。是什么让它与众不同?让我们想象一下我们需要在程序中实现“几个月”。 看起来没有问题,对吧?我们只需要确定任何月份都有哪些属性。也许我们首先需要月份的名称和其中的天数。解决方案看起来很简单:
public class Month {
private String name;
private int daysCount;
public Month(String name, int daysCount) {
this.name = name;
this.daysCount = daysCount;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getDaysCount() {
return daysCount;
}
public void setDaysCount(int daysCount) {
this.daysCount = daysCount;
}
@Override
public String toString() {
return "Month{" +
"name='" + name + '\'' +
", daysCount=" + daysCount +
'}';
}
}
整个沙邦!我们有一个Month
类、必填字段、getter/setter 和toString()
. 当然,除非我们需要添加equals()
和hashCode()
获得完全的幸福 :) 但是这里我们有一个概念上的问题。您可能还记得,OOP 的主要优点之一是它可以轻松地为现实世界中的实体建模。一把椅子、一辆汽车、一颗行星——所有这些来自日常生活的概念都可以在抽象的帮助下很容易地在程序中表示。问题在于一些现实世界的实体具有严格限制的值范围。一年只有 4 个季节。一个八度只有8个音符。日历只有 12 个月。Ocean's 11 的 Danny Ocean 只有 11 个朋友(尽管这无关紧要 :))重要的是普通的 Java 类无法对这些实体建模并强制执行它们的自然限制。我们的Month
类具有所有必填字段。但是如果另一个程序员使用它,没有人能阻止他或她创建完全疯狂的对象:
public class Main {
Month month1 = new Month("lolkek", 322);
Month month2 = new Month("yahoooooooooooo", 12345);
}
如果这出现在我们的代码中,就不容易找到罪魁祸首了!一方面,创建对象的程序员可能会意识到Month
类的意思是“一年中的一个月”,而不是写这种废话。另一方面,程序员只是在利用类设计者提供的能力。是否可以指定任意名称和天数?这正是我们得到的。那么在这种情况下我们应该怎么办呢?老实说,在 Java 1.5 发布之前,程序员必须发挥创造力 :) 在那些日子里,他们创建了这样的结构:
public class Month {
private String name;
private int daysCount;
private Month(String name, int daysCount) {
this.name = name;
this.daysCount = daysCount;
}
public static Month JANUARY = new Month("January", 31);
public static Month FEBRUARY = new Month("February", 28);
public static Month MARCH = new Month("March", 31);
@Override
public String toString() {
return "Month{" +
"name='" + name + '\'' +
", daysCount=" + daysCount +
'}';
}
}
在这里,我们将月数从 12 减少到 3,以使示例更短。这样的设计使得解决问题成为可能。创建对象的能力仅限于私有构造函数:
private Month(String name, int daysCount) {
this.name = name;
this.daysCount = daysCount;
}
使用该类的程序员不能简单地创建Month
对象。他们不得不使用类开发人员提供的最终静态对象。例如,像这样:
public class Main {
public static void main(String[] args) {
Month january = Month.JANUARY;
System.out.println(january);
}
}
但是,Java 开发人员提请注意现有问题。当然,程序员能够使用该语言中可用的工具想出一个解决方案真是太好了,但这看起来并不容易!需要一个明显的解决方案,即使对于新手也是如此。因此Enum
出现在Java中。基本上,Enum
它是一个提供一组有限对象值的 Java 类。这是它的样子:
public enum Month {
JANUARY,
FEBRUARY,
MARCH
}
在定义中,我们指出这Enum
是一个 Java 类,但事实真的如此吗?是的,我们甚至可以验证它。例如,尝试让我们的Month
枚举继承一些其他类:
public abstract class AbstractMonth {
}
// Error! The extends clause cannot be used with an enum
public enum Month extends AbstractMonth {
JANUARY,
FEBRUARY,
MARCH
}
为什么会这样?当我们写:
public enum Month
编译器将此语句转换为以下代码:
public Class Month extends Enum
如您所知,Java 不支持多重继承。因此,我们不能继承AbstractMonth
。如何Enum
使用这个新结构?它与带有字段的旧构造有何不同static final
?好吧,举个例子,旧的结构不允许我们在switch
语句中使用我们自己的一组值。想象一下,我们想要创建一个程序来提醒我们每个月庆祝的假期:
public class HolidayReminder {
public void printHolidays(Month month) {
switch (month) {
// Error!
case JANUARY:
}
}
}
如您所见,编译器在此处抛出错误。但是一旦enum
出现在 Java 1.5 中,一切就变得简单多了:
public enum Month {
JANUARY,
FEBRUARY,
MARCH
}
public class HolidayReminder {
public void printHolidays(Month month) {
switch (month) {
case JANUARY:
System.out.println("New Year's Day is January 1st!");
break;
case FEBRUARY:
System.out.println("Valentine's Day is February 14th!");
break;
case MARCH:
System.out.println("Saint Patrick's Day is March 17th!");
break;
}
}
}
public class Main {
public static void main(String[] args) {
HolidayReminder reminder = new HolidayReminder();
reminder.printHolidays(Month.JANUARY);
}
}
控制台输出:
New Year's Day is January 1st!
请注意,对对象的访问Enum
仍然是静态的,就像 Java 1.5 之前一样。我们不需要创建Month
对象来访问月份。使用枚举时,不要忘记它Enum
是一个完整的类,这一点非常重要。这意味着,如果需要,您可以在其中定义构造函数和方法。例如,在前面的代码片段中,我们简单地指定了值:JANUARY、FEBRUARY、MARCH。但是,我们可以Month
像这样扩展我们的枚举:
public enum Month {
JANUARY("January", 31),
FEBRUARY("February", 28),
MARCH("March", 31),
APRIL("April", 30),
MAY("May", 31),
JUNE("June", 30),
JULY("July", 31),
AUGUST("August", 31),
SEPTEMBER("September", 30),
OCTOBER("October", 31),
NOVEMBER("November", 30),
DECEMBER("December", 31);
private String name;
private int daysCount;
Month(String name, int daysCount) {
this.name = name;
this.daysCount = daysCount;
}
public static Month[] getWinterMonths() {
return new Month[]{DECEMBER, JANUARY, FEBRUARY};
}
public static Month[] getSummerMonths() {
return new Month[]{JUNE, JULY, AUGUST};
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getDaysCount() {
return daysCount;
}
public void setDaysCount(int daysCount) {
this.daysCount = daysCount;
}
@Override
public String toString() {
return "Month{" +
"name='" + name + '\'' +
", daysCount=" + daysCount +
'}';
}
}
在这里,我们提供了enum
2 个字段(月份名称和天数)、使用这些字段的构造函数、getter/setter、方法toString()
和 2 个静态方法。如您所见,这没有问题。再一次,Enum
真的是一个完整的类:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
System.out.println(Arrays.toString(Month.getSummerMonths()));
}
}
控制台输出:
[Month{name='June', daysCount=30}, Month{name='July', daysCount=31}, Month{name='August', daysCount=31}]
最后,我想推荐一本非常有用的Java书籍,即Joshua Bloch的《Effective Java》。 作者是 Java 的创造者之一,因此您绝对可以相信他关于如何正确和熟练地使用语言工具的建议 :) 关于我们的课程,我建议您特别注意本书关于Enum
. 阅读愉快!:)
GO TO FULL VERSION