Java—快速归类整型常数—枚举

枚举简介

目前计算机的功能不仅仅局限于加减乘除等数值计算,而且还被拓展至非数值数据的处理,例如,天气、性别、星期几、颜色、职业等这些都不是数值数据。在程序设计中,往往存在着这样的“数据集”,它们的数值在程序中是稳定的,而且“数据集”中的元素是有限的,通常用一个数组代替一种状态,如0代表红色(red),1代表绿色(green),2代表蓝色(blue),但这种非数值的处理方式不直观,可读性比较不强。能否直接使用自然语言中相应含义的单词来代表某一种状态呢?

在JDK 1.5,Java引入的一种新类型——枚举类型(Enumerated Type),就是来解决此类问题的。定义时,它使用enum关键字标识。例如,表示一周的星期几,用SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY、SATURDAY就可表示为一个枚举。而在本质上,SUNDAY就表示0,MONDAY就表示1,…,SATURDAY就表示6。由此可见,,相比于“无明确含义”的纯数字“0, 1,2,…,6”,枚举所用的自然表示法让程序更具有可读性。

自定义的枚举

在未出现枚举关键字前如何使用枚举功能(Color.java)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
【代码详解】
第08行使用getName()方法来获得私有属性成员name。

第09行使用setName()方法对类中的私有属性成员name进行赋值。为了区分setName()方法中形参name和类中的同名数据成员name,用this对象引用来表明赋值运算符(=)左侧的name是来自类中。

第10行是对Color类中构造方法的声明,其内部通过调用setName()方法,对新对象取值进行初始化赋值。由于第10行声明的构造方法是私有访问类型的,这意味着,外界(其他类中)是无法通过调用构造方法来创建对象的,例如,下面的语句在是非法的。
在这里插入图片描述
私有构造方法的访问类型决定了只能在类Color 内部构造新的对象。在第04~06行中,通过对3个Color的对象声明,可得到3个不同的Color对象,分别是RED、GREEN和BLUE,其取值分别为“红色”、“绿色”和“蓝色”。使用publicstatic final这三个关键字来作为所创建对象的修饰符,表明这三个对象是“公有的、静态的、不可更改的”常量对象。

第12~18行设置静态方法getInstance()。通过此方法,可以通过设置不同的数值(如0,1, 2),调用得到相应的颜色(如“红色”、“绿色”和“蓝色”)。第17行的语句也是重要的,因为无法保证用户就一定“规规矩矩”地输入合法的参数(即仅仅输入0-2这三个值),如果用户输入的值在0-2之外的值,那么就会返回一个空引用(null),这本质上是一个容错性的语句,提供程序的健壮性。

第20~30行,创建三个对象引用c1、c2和c3,分别通过调用静态方法getInstance()来获得类Color创建的三个静态对象。通过控制getInstance()方法参数的不同,来获得不同的颜色。要注意到getInstance()方法是静态的,所以它的访问方法是“类名.方法名()”。由于这种方法与对象无关,且getInstance()方法的访问权限为“public”,所以下面的在外类中访问这些对象的方法是合法的。
在这里插入图片描述
由此可见,通过复杂的设置,在类Color中生成的常量对象可在外类(如TestColor)中得以访问。

【范例分析】
由于在JDK1.5之前,Java虚拟机中并没有关于枚举类型的包,所以使用枚举的时候,只能够从自己创造的构造方法中引入。在本例中,对构造方法进行私有化之后,如果想访问自己构建的类似于C/C++中枚举数据,需要通过static属性得到Color的实例化对象,或者用静态方法getInstance()来取得。由此可见,对于这种“自定义的枚举”类型,使用过程繁琐而容易出错。而且,通过上述方式实现的枚举,还会存在一系列的问题,如调用过程容易出错,语义存在模糊性而影响代码的可读性。

Java中的枚举

常见的枚举定义方法

在枚举类型中,一般的定义形式如下。
在这里插入图片描述
其中enum是Java中的关键字。在枚举值表中应罗列出所有的可用值,这些值也称为枚举元素。例如。
在这里插入图片描述
这里定义了一个枚举类型WeekDay,枚举值共有7个,即一周中的7天。凡被说明为WeekDay类型变量的取值,只能是这7天中的某一天。

枚举变量也可用不同的方式说明,如先定义后说明、定义的同时说明或直接说明。设有变量a、b、c被定义为上述的枚举类型WeekDay,可采用下述任意一种方式。
在这里插入图片描述

在程序中使用枚举

当创建了一个枚举类型之后,就意味着可在今后的代码中进行调用。调用先前定义的枚举类型,同其他的调用语句一样,需要声明该类的一个对象,并通过对象对枚举类型进行操作。

在JDK1.5以前,通过对静态属性声明的方法创造了一个自定义的枚举类型。这样定义出来的枚举,操作非常不便,当修改任何一个对象时,需要对多处进行改动。如果使用Java中的Enum方法,代码量将大大减少。同时,代码的可修改性与可读性也会相应地提高。

在Java中使用枚举(EnumColor.java)
在这里插入图片描述
在这里插入图片描述
在第01行中,定义Enum数据类型MyColor,其中设置的枚举值表分别为“红色、绿色、蓝色”。07、10和13行分别定义了枚举变量c1、c2和c3,而c1、c2和c3只能是MyColor枚举元素中的3个值的一个,它们通过“枚举名.枚举值”的方法获得。08、11和14行分别输出获得的枚举值。

通过Java提供的枚举类型,可以很轻松地调用枚举中的每一种颜色。而且,使用此方式还可以避免之前使用接口实现枚举的操作。

在switch语句中使用枚举

使用Enum关键字创建的枚举类型,也可以直接在多处控制语句中使用,如Switch语句等。在JDK1.5之前,switch语句只能用于判断字符或数字,它并不能对在枚举中罗列的内容进行判断和选择。而在JDK 1.5之后,通过Enum创建的枚举类型,也可以被Switch判断使用。

在switch中使用枚举(EnumSwitch.java)。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
本例中通过switch调用枚举类型MyColor完成对于枚举类型的筛选。第7~24行均是对于switch语句的使用。由于Java采用的是Unicode的字符串编码方式,所以枚举值也可支持中文。

由本例可以看出,在JDK1.5之后,switch同样可以用来判断一个枚举类型,并对枚举类型做出有效选择。这样在今后的程序写作过程中,就能够避免枚举类型多而繁琐的选择问题。这有助于增加代码的可读性和延伸性。

枚举类和枚举关键字

举类型的出现,有助于简洁程序的代码量,减少出错量。在大多数情况下,枚举类和枚举关键字是相互依存的。枚举关键字是定义枚举类型时必不可少的声明,而枚举类则是规定的枚举类型母类。

枚举类

枚举类(Enum类)是在Java.lang包下定义的一个公共类,它的作用是用来构造新的枚举类型。这是在JDK1.5之后Java推出的一个新的类,用来弥补关于枚举这一常用集合在Java中的不足。同时, Enum类中的构造方法可方便对代码的操作。

在JDK API中可以看到,在Enum类中定义了大约十多个方法,每一种方法都是对用Enum创建的枚举对象实施操作,所以Enum类是一个完整的类型。它拥有自己的方法,当创建一个关于Enum的类型对象时,便可调用其中的方法来完善对于枚举类型的操作。下表列出了Enum类中的主要方法。
在这里插入图片描述
通过values()方法得到对象取值(ValueOfEnum.java)
在这里插入图片描述
在这里插入图片描述
本例是通过调用Enum类中的方法values()来得到枚举类型中各个对象的取值。

第01行声明了一个enum类型的对象MyColor。第06行声明一个enum类型的数组allColor,它通过values()方法来获得MyColor的各个枚举值“红色, 绿色, 蓝色”。

在第07~10行中, 使用for-each循环语句中将数组allColor值依次输出。

枚举关键字(enum)是定义的一个枚举类型。实际上,在此次定义的过程中,通过enum关键字相当于定义了一个类,并且此类将继承自Enum类。大家可能会注意到,在上面的Enum类“方法概要”的表格中,并没有列出来values()方法。实际上,这是编译器添加的隐式方法,如果一个对象被声明为enum类型,编译器会自动给该类型添加一个隐含的方法values(),它的原型为:

public static E[] values();

实际上,Enum类里还包含有很多种方法,对于不同方法的作用,需要大家逐步在程序实践中深入了解。

枚举关键字

被定义的对象拥有Enum类中的构造方法的使用权。如上面的Enum类“方法概要”的表中所示,在Enum类中的构造方法中是受保护的,实际上对于每一个枚举的对象一旦声明之后,就表示自动调用此构造方法,所有的编号方式均采用自动编号的方式进行。在没有对编号做出特殊声明时,Java虚拟机一般将对被创建的枚举类型对象自动编号,编号从0开始。例如,对于下面的枚举定义
在这里插入图片描述
“红色”对应的编号是0,“绿色”对应的编号是1,相应的,“蓝色”对应的编号是2。下面的范例验证了这一点。

通过调用Enum类中的ordinal()方法,输出枚举类型中每一个对象的编号(OrderOfEnum.java)
在这里插入图片描述
在这里插入图片描述
第01行声明了一个enum类型的对象MyColor。第06行声明一个enum类型的数组allColor,它通过values()方法来获得MyColor的各个枚举值“红色, 绿色, 蓝色”。

在第07~10行中,使用for-each循环语句中将数组allColor元素对应的编号遍历输出。其中第09行引入了在Enum类中包含的两种方法:name()和ordinal(),分别获得不同对象的名称及编号。通过本范例可很清楚地得到本例中定义的MyColor枚举类型中各项元素的编号。

枚举类与枚举关键字的联系

在JDK API(应用程序接口,Application Programming Interface,简称API)中,找到Java.lang下的Enum类,可看到许多关于Enum类的方法,可参见方法概要表。实际上,使用enum关键字,相当于定义了一个类,该类将继承自Enum类。而在Enum类中的方法,访问权限都是保护(protected)类型的,因此这些方法都可以在enum关键字定义的对象中直接使用。例如,调用Enum类中存在valueof()方法,该方法返回带指定名称的指定枚举类型的枚举常量。在下面的范例中,先用enum关键字定义了一个枚举对象Color,然后调用Enum类中的valueof()方法。

调用Enum类中的valueof()方法(ValueOfEnum2.java)
在这里插入图片描述
在这里插入图片描述
在第06行中,定义Color枚举类型对象c。Color枚举类型调用在Enum类中方法valueof(),这是一个静态方法,用于返回指定枚举类中指定名称的枚举值。在本例中,方法valueof()的第一个参数“Color.class”,用来返回Color的类型——枚举类型,第二个参数是字符串常量“红色”。

valueof()方法的原型是比较复杂,如下所示。
在这里插入图片描述
该方法的第一个参数enumType,是枚举类型的Class对象返回一个常数。在本例中,可通过传递Color.class类型来完成参数的传入。

该方法的第二个参数name,是一个常量字符串名称。这个参数必须在与该枚举类中声明枚举值时所用的标识符完全匹配,不允许使用额外的空白字符。例如,在本例中,如果使用“红 色”,字符串中间有空格,则会返回方法无法识别数据源的错误。
在这里插入图片描述
该方法的返回值为<T extends Enum> T,它是指定枚举类型的枚举常量。从返回类型中的修饰符“extends Enum”,可以看出该方法返回的枚举类型继承自类Enum。而该方法中出现的“T”表示的是模版类型参数,在本例中,被实例化为Color。此外,注意到由于valueof()为静态方法,所以第06行的访问方式为“类名.方法名()”。

直接通过枚举取得指定内容会更加简单。

下面我们总结一下使用枚举类的注意事项。

⑴ 如果enum定义的枚举类访问权限定义为public,则需要单独形成一个.java文件,即不可与包含main方法的public类同处于同一个文件。如果enum定义的枚举类访问权限为默认类型,即enum关键字前没有修饰符,则enum定义的对象可在同一个包里访问,无需重复定义。例如,前面的范例都在一个包中,则所定义的枚举类型Color(MyColor)事实上仅需要在一个.java文件定义即可,其他用到这个枚举类型的文件可以通过导入(import)的模式来复用这个定义。

⑵ 使用enum定义的枚举类,默认继承于java.lang.Enum类。使用enum定义的枚举类,默认会使用final修饰,因此该类无法派生子类。关于“派生与继承”的概念我将在后期文章中详细讲到。

⑶ 使用enum定义的枚举类,其所有的枚举值(实际上是该类的实例)必须在枚举类的第一行显示列出,否则这个枚举类将永远不能产生实例。在列出这些实例时,系统会自动添加public static final修饰。

⑷ 所有使用enum定义的枚举类,都会由系统隐式提供一个values()方法,该方法可方便地遍历所有的枚举值。

类集对于枚举的支持

EnumMap

在没有讲解EnumMap类之前,我们先来说明一下Map (映射)概念的含义。Map(映射)本质上是一种把键和值<key,value>一一对应的映射集合,它的每一个元素都包含一对键对象和值对象,如下图所示。 Map关系类似于学生的学号和姓名的对应关系,找到学号,就能很快找到学生的姓名,在这个映射表中,如果来了新的学生,我们可以很自然地添加一个新的<学号,姓名>映射元素。
在这里插入图片描述
Map是Java中的一个类, EnumMap是Map接口的子类,它被视为专门为枚举类型量身定做的Map实现。虽然使用其他Map实现(例如HashMap)也可以完成枚举类型实例到值的映射,但使用EnumMap会更为高效。这是因为EnumMap只接收同一枚举类型的实例作为键值,枚举类型实例的数量有限并且相对固定,因此EnumMap使用数组来存放与枚举类型对应的值,这使得EnumMap的存取效率比较高。由于EnumMap继承自Map,Map中的方法都可以在EnumMap使用,通过调用EnumMap中的方法可方便地完成对枚举对象的操作。

使用EnumMap操作枚举类中实例(EnumMapDemo.java)
在这里插入图片描述
在这里插入图片描述
本例的目的在于让大家了解怎样使用EnumMap类集。由于EnumMap类和 Map类不是java.lang默认加载的包库。所以,在第01~02行,导入在Java包lang的EnumMap类和 Map类。第03行定义了枚举类型Color,其包含的枚举值分别为“红色,绿色,蓝色”。

第08行创建了一个EnumMap类的对象 eMap。EnumMap<KeyType,NameType>是一个模板类,这里它们的两个类型参数KeyType和NameType分别被实例化为Color和String,需要特别注意的是, EnumMap的构造方法参数不能为空,需要指定一个枚举类,本例中参数为Color.class,这个.class对象给EnumMap类提供有关枚举enum的元信息(meta information),这些信息主要包括枚举值的数量,这样是为EnumMap创建内置数组来存储这些枚举值提供方便。

第09行,将“Color.红色”作为键(key),“RED”作为映射的值(value),二者一起构成一对<key, value>映射,然后利用EnumMap中的put()方法来操作完整这种映射。第09~11行完成类似的操作。

第09~16行,通过for-each循环,将eMap所拥有元素的<key,value>依次输出。其中, EntrySet()是定义在Map类中的一个方法,其作用是返回整个映射实例(entry)的集合。EntrySet()方法名中的Set不能理解“设置”,其表示的含义是“集合”。

EnumSet

在讲解EnumSet之前,我们需要先了解明白“Set(集合)”和“Map(映射)”的区别。Map是一个二元组的集合,Map中的每个元素都是由<key,value>两个属性组成。Map提供从key到value的映射。通过key可以很容易地找到其对应的value。一个Map中不能包含相同的key,即每个key必须是独一无二的,且每个key只能映射一个value。这里需要说明的是,value的值是可以重复的。

而对于Set,它是一个一元组集合,其包含一系列不可重复的数据,即对于Set中任意两个元素e1和e2,都有e1.equals(e2)=false。也可以将Set理解为,其元素中只包含key,而key是不允许重复的。Set中最多有一个null(空)元素。Set可进行集合的各种操作(如交、并、补等)。

EnumSet可视为专门用于处理枚举(enmu)类型的Set类集。EnumSet本身是Set接口的子类,但是在此类中并没有提供任何的构造方法定义,这表明其构造方法被私有化了。此外,需要注意的是,所有对此类方法的操作均属于静态操作

测试EnumSet静态方法allOf()(EnumSetDemo01.java)。
在这里插入图片描述
在这里插入图片描述
本例的目的在于,测试EnumSet中定义的静态方法,通过对定义在EnumSet中方法的调用,了解EnumSet的特性和作用。

由于EnumSet类和 Iterator类不是java.lang默认加载的包库。所以,在第01~02行,分别导入这两个类。第04行定义了枚举类型Color,其包含的枚举值分别为“红色,绿色,蓝色”。

第09行定义了EnumSet类型的对象eSet,在这个集合中,每个元素的类型都是Color。通过Color实例化EnumSet< ElemType>这个模板类(或称类集)中尖括号<>内的元素类型ElemType。

allof()是EnumSet中的内部静态方法,用来将这个方法参数中所指向的全部内容设置到集合。在本例中,allof()方法的参数为“Color.class”,也就是说将枚举类型Color所包含的全部取值作为eSet的元素。枚举类型Color 的枚举值“红色, 绿色,蓝色”,它们取值各不相同,满足集合的要求。

在第10行,定义了一个指向枚举类型Color类型的迭代器(Iterator)。迭代器,有时也称游标(cursor),在某种意义上,它类似于C/C++中的“指针”,用于遍历集合类的元素。它可以把访问逻辑从不同类型的集合类中抽象出来,从而避免向客户端暴露集合的内部结构。设计人员仅需了解集合遍历的接口,而无需关注集合类中的内存分配的实现细节。

在第11行,使用hasNext()方法,判断集合中是否还有元素。如果有,迭代器通过iter.next()方法(代码13行)指向下一个集合元素,如果没有元素了,while循环中的逻辑判断条件 iter.hasNext()返回false,终止循环。

调用noneOf()方法,对同样包含在EnumSet类中的不同方法的调用,来测试EnumSet方法(EnumSetDemo02.java)。
在这里插入图片描述
在本例第09行使用noneOf()方法,表示Color.class中的元素一个也不取。换句话说,本例中的eSet为空集合。因此,代码第11~14行,无法输出任何结果。

深入了解枚举

枚举的构造方法

枚举的使用非常灵活,它可应用于代码中的各个角落,只要定义的对象具有枚举的形式,均可使用枚举对其进行定义。这样在减少代码量的同时,也可增加代码的可读性和可操作性。

在前面已经讲解过简单的枚举类型定义方法,例如,我们定义枚举Color,其枚举值为“红色、 绿色、蓝色”,相应的代码如下所示。
在这里插入图片描述
在一般情况下,我们似乎没办法控制每个枚举类型的值。例如,Java虚拟机一般将对被创建的枚举类型对象自动编号,编号从0开始。 “红色”的编号为0,“绿色”的编号为1,“蓝色”的编号为2。那能不能对enum类型的数据实施“改造”,达到用户个性化的需求呢?当然可以。下面的范例就通过定制enum内部方法,来达到个性化需求。

定义枚举的构造方法 (DeepEnum.java)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在枚举中可直接定义方法。在本例中,对枚举类型NewColor定义了1个构造方法NewColor()和4个普通方法,分别是getName()和setName()、getIndex()和setIndex()。

第1行中定义了3个新创建的枚举类型NewColor的枚举值:“RED、GREEN、BLUE”,实际上,这三个枚举值是NewColor枚举类的三个实例化对象。在这里,有两点需要读者注意:①枚举类定义的对象必须出现在该类有效代码的第1行,如果将第1行代码移至其他行之后,编译无法通过,因为编译器无法解析 “RED、GREEN、 BLUE”等对象。②一旦枚举类的构造方法定义之后,那么所有的枚举对象都必须显式调用此构造方法,例如,“RED(“红色”,4), GREEN(“绿色”,5),BLUE(“蓝色”,6)”都是在调用第05~08行的二参构造方法。

第03~04行,定义了枚举中的两个私有数据成员name和index。

第05~08行,定义了NewColor()这个私有化(private)的构造函数。这样就限制了外部是不能调用枚举的构造方法。

第09~14行,定义了一个普通的方法getName(int index),通过枚举的索引获得其对应的名称。其中,第10~12行,通过一个for-each循环来来查询满足索引条件的枚举值。由于NewColor是枚举类型,所以它也可以用编译器提供的内置隐式方法values()来获得全部枚举值。第13行代码也是有意义的,如果用户没有输入合法的整数索引(针对本例,合法的范围在4~6),则返回null,这样就提供了程序的健壮性。

第15行的getName()方法用来返回枚举对象的name值。17~24行的setIndex(intindex, String name)方法,通过index来定位要修改name的枚举对象,然后将新的index赋给当前对象。

第25行的getIndex()方法用来返回枚举对象的index值。第25~34行定义了setIndex(int index, String name)方法,通过枚举对象的name定位枚举对象,然后修改其对应的index值。

第41~43行,分别输出枚举对象“RED、GREEN、 BLUE”的各自的index和name。这里需要读者注意的是,“RED、GREEN、 BLUE”是枚举类型NewColor的枚举实例,这些枚举实例是公有的静态对象(进一步地说,它们可视为枚举类的属性成员),因此其访问类型可以是“枚举类名.属性成员名”,而这些对象可很自然地可通过点操作符“.”调用它们的公有方法,例如getIndex()和getName()等。

第46行和第48行分别重新设置了指定枚举对象(通过index或name来确定)的name和index属性值。由于setName(int index, String name)和setIndex(intindex, String name)方法均属于静态方法,所以它们可以通过“枚举类名.方法名”的格式来调用这些方法。

枚举的接口

1. 为什么需要接口

在讲解枚举的接口之前,我们先简要描述一下接口的概念,更为详细的描述,我会在后期介绍。我们先用生活中的一个实例来说明接口的概念。大家常用U盘来存储、传递自己的文件。而目前生产U盘的厂商很多,对于每一个类型的U盘,用户无需关注其内部的实现细节,只要它们的USB接口符合规范,就能在不同的计算机设备上使用。这种规定USB接口的模式,对商家(实现者)和用户均有利,对商家而言,他们可以自主研发具备自己特色的产品,而对用户而言,它们在体验不同类型U盘的同时,统一的USB接口又让他们在使用时没有额外的负担。

下面来讲Java中为什么需要接口。在《论语·为政》中:“子曰:‘君子不器’”。孔夫子告诉世人:君子不能像器具一样,只有一种用途。而在我们的程序设计实践中,我们也希望设计一个完善的类,它不能只有一种用途,随着程序开发的不断的深入,希望它能表现出多面功能性。但为了避免多重继承中带来的负面作用,例如,多重继承中的数据成员构成的“恐怖菱形”等,Java禁用了C++中常用的“多重继承”。

在前文中,我们已经提到,所有的枚举事实上都继承自java.lang.Enum类。这表明枚举已有了一个“父类”,那它不能再继承其他类,那么是不是枚举就“注定孤独地”使用继承自Enum类中的方法呢?Java语言的设计者是聪明的,他们为用户“关闭了一扇门”,但又为“开启了另外一扇窗”,那这扇窗就是Java中的一个重要概念——接口(Interface)。

在Java的一个类中,既包括数据成员(即属性),又包括对这些属性的操作(即方法)。

而在Java的一个接口中,在某种程度上,它可视为一个简化版本的“类”,其仅包括一系列方法和一些不可更改的静态常量。Java中的接口不能直接拿来实例化任何对象,因为在其内仅仅提供了方法的声明,即只有方法的特征而没有方法的具体实现,它不具备实例化对象的条件。换句话说,接口“生来”就是要被继承的,在继承中被具体化实现。接口中的这些方法可在不同的地方被不同的类实现,从而可表现出具有不同的“个性化”行为(功能)。

Java语言中的接口,只是对要实现该接口方法的所有类提出了一个共享的固定格式的协议(protocol)。这些协议固定了在其内的静态常量和方法签名(方法名+参数列表)形式,而继承这个接口的类,就可在其类中对这些继承而来的方法,“独立自主、自由发挥”地实现这些方法。

Java语言虽然不支持一个类有多个直接的父类(即不支持多继承),但一个类却可以实现(implements)多个接口。接口弥补了Java类不能多继承缺点,单继承和多接口的双重设计既保持了类的数据安全,也间接实现了多继承。Java的“另外一扇窗””就在这里开启。

2. 枚举中如何实现接口

枚举与普通类一样,可以实现一个或多个接口。当一个枚举实现一个接口之后,各个枚举对象都必须分别实现接口中的抽象方法。

创建枚举接口。本例是新创建的一个接口,其中声明了一个方法getColor()方法(ColorInterface.java)。
在这里插入图片描述
本例定义了一个ColorInterface的接口,接口内含一个getColor ()方法,目的在于得到一个枚举类中的内容。但在接口中的getColor ()方法,仅有方法的原型,而没有具体的实现。

对新建接口抽象方法的调用。对接口中定义的抽象方法的调用,并且在后期声明的枚举类型中,任何一个对象都必须分别实现接口中的抽象方法(NewColor2.java)。
在这里插入图片描述
在枚举类里面实现接口的方法时,必须为每个枚举对象(如本例的“红色、绿色、蓝色”)实现一次从接口ColorInterface继承而来的方法。第03~23行完成了这样的操作,这里需要注意的是,每个枚举值之间是用逗号“,”隔开的,最后一个才是用分号“;”结尾的。之所以这样做,其实是为了保证枚举类定义对象必须出现在该类的第一行。读者可以理解为03~23行其实就是一条复合大语句,本质上就是一条语句。

上述的每个枚举值在调用getColor()方法时,得到都会有类似的结果——返回颜色对应的英文大写字符串,这是因为每个枚举对象对getColor()方法体的实现部分是类似的。事实上,每个枚举值在实现getColor()方法时,可以表现出不一致的行为来,大家可以自行尝试一下。

通过代码测试上面实现的接口类型(TestColor.java)
在这里插入图片描述
保存上述3个范例文件,并运行程序,结果如下图所示。
在这里插入图片描述
代码第05~08行,使用for-each循环,输出NewColor2枚举对象值的编号和名称。第07行代码中,枚举对象具备了getColor()方法,所以可以输出与中文名称对应的英文名称。

通过测试可以看到,枚举中的任何一个对象都分别实现接口中的方法后,才能测试成功。

在枚举中定义抽象方法

通过上面的学习可知,枚举可以实现接口。在接口中定义的方法,由于只有方法原型,没有具体实现,这些方法可视为是“抽象的”方法,不管用不用修饰符abstract都是一样。实际上,Java也可以在枚举中直接定义一个或多个抽象方法。需要注意的是,这种情况下,需要为枚举中的每个对象单独地实现此方法。

定义枚举的抽象方法。直接在枚举中定义抽象方法,其中每个对象都必须单独实现此方法(AbstractEnum.java)。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
本例与之前范例的运行结果是一样的。所不同的是,抽象方法getColor()是在枚举类中定义的(第25行)。在Java编程实现上,所谓抽象方法,就是用关键字abstract修饰且没有实现主体的方法。这个抽象方法getColor()需要在枚举对象“红色、绿色、蓝色”中一一给予单独实现,否则编译出错。

1. 枚举使用时的注意事项

Java为枚举扩展了非常强大的功能,但是在使用过程中,常会错用枚举,下面2点是使用过程中的注意事项。

⑴ 枚举类型不能用 public 和 protected 修饰符修饰构造方法。构造方法的权限只能是private或者friendly,friendly是当没有修饰符时的默认权限。因为枚举的这种特性,所以枚举对象是无法在程序中通过直接调用其构造方法来初始化的。

⑵ 定义枚举类型时,如果是简单类型,那么最后一个枚举值后可以不加分号。但是如果枚举中包含有方法,那么最后一个枚举值后面代码必须要用分号“;”隔开。

2. 枚举类不可以被继承

在枚举类内部可以定义一个或多个抽象方法时,那这个枚举类为什么不能用abstract修饰呢?如果一个枚举类用abstract修饰,那么就说明需要其他类继承这个所谓的“抽象枚举类”,而在Java中,规定枚举是不能被继承的。因此,枚举不能用abstract修饰,只能在每个枚举的实例实现每个抽象方法。

Java—核心技术类的封装、继承与多态

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值