13.1 枚举类型
使用枚举类型,可以取代前面学习过的定定义常量的方式,同时枚举类型还赋予程序在编译时进行检查的功能。
13.1.1 使用枚举类型设置常量
设置常量时,我们通常将常量放置在接口中,这样在程序中就可以直接使用。该常量不能被修改,因为在接口中定义常量时,该常量的修饰符为 final与 static。常规定义常量的代码如下:
public interface Constants {
public static final int Constants_A=1;public static final int Constants_B=12;
枚举类型出现后,逐渐取代了上述常量定义方式。使用枚举类型定义常量的语法如下:
public enum Constants{
Constants_A,Constants_B,
}
其中,enum是定义枚举类型的关键字。 当需要在程序中使用该常量时,可以使用Constants.
Constants _A 来表示。
【例13.1】分别创建四季的接口常量和枚举,比较两者的使用场景
分别创建SeasonInterface接口和SeasonEnum枚举来定义四季常量,在SeasonDemo类中创建两个 printSeason()方法,分别以SeasonInterface接口常量和S easonEnum枚举作为参数,打印传入的月份名称。尝试在调用printSeason()方法时使用接口常量值以外的数字“冒充”常量值。
13.1.2 深入了解枚举类型
枚举类型较传统定义常量的方式,除具有参数类型检测的优势外,还具有其他方面的优势。用户可以将一个枚举类型看作是一个类,它继承于java.lang.Enum类,当定义一个枚举类型时,每一个枚举类型成员都可以看作是枚举类型的一个实例,这些枚举类型成员都默认被final、public、static修饰,所以当使用枚举类型成员时直接使用枚举类型名称调用枚举类型成员即可。
由于枚举类型对象继承于java.lang.Enur m类,所以该类中一些操作枚举类型的方法都可以应用到枚举类型中。
1.values()方法
枚举类型实例包含一个values()方法,该方法将枚举中所有的枚举值以数组的形式返回。
【例13.2】打印四季枚举中的所有枚举值在项目中创建ShowEnum类,在该类中使用枚举类型中的 values()方法获取四季枚举中的所有枚举值并打印出来。
2.values()方法与compareTo()方法
枚举类型中静态方法valueOfO可以将普通字符串转换为枚举类型,而compareTo()方法用于比较两个枚举类型对象定义时的顺序。
【例13.3】使用字符串创建一个季节的枚举值,关判断季节的位置
创建 EnumMethodTest类,在主方法中创建字面值为“SUMMER”的季节枚举,让创建出的枚举与四季枚举的每一个值多对比,判断“SUMMER”所在的位置。
3.ordinal()方法
枚举类型中的ordinal()方法用于获取某个枚举对象的位置索引值。
【例13.4】输出每一个季节的索引位置
在项目中创建EnumIndexTest类,在该类中使用枚举类型中的ordinal0方法获取枚举类型成员的位置索引。
4.枚举类型中的构造方法
在枚举类型中,可以添加构造方法,但是规定这个构造方法必须被private修饰符所修饰。枚举类型定义的构造方法语法如下:
从枚举类型构造方法的语法中可以看出,天无论是无参构造方法还是有参构造方法,修饰权限都为 private。定义一个有参构造方法后,需要对枚举类型成员相应地使用该构造方法,如Constants A("我是枚举成员 A")和ConstantsD(3)语句,相应地使使用了参数为 String型和参数为int型的构造方法。然后可以在枚举类型中定义两个成员变量,在构造方法中为这两个成员变量赋值,这样就可以在枚举类型中定义该成员变量的getXXX0方法了。
【例13.5】为四季枚举创建构造方法,记录每一个季节的特征
在四季枚举中创建一个字符串类型的备注属性,并创建该属性的Getter方法,在枚举构造方法中为备注属性赋值,最后输出每一个季节枚举的备注值。
13.1.3 使用枚举类型的优势
枚举类型声明提供了一种对用户友好的变量定定义方法,枚举了某种数据类型所有可能出现的值。总结枚举类型,它具有以下特点:
①类型安全。
②紧凑有效的数据定义。
③可以和程序其他部分完美交互。
④运行效率高。
13.2 泛型
泛型实质上就是使程序员定义安全的类型型。在没有出现泛型之前,Java也提供了对Object类型的引用“任意化”操作,这种“任意化”操作京就是对Object类型引用进行向下转型及向上转型操作,但某些强制类型转换的错误也许不会被编译器捕捉,而在运行后出现异常,可见强制类型转换存在安全隐患,所以在此提供了泛型机制。
13.2.1 回顾向上转型与向下转型
13.2.2定义泛型类
Object类为最上层的父类,很多程序员为了使程序更为通用, 设计程序时通常使传入的值与返回 的值都以 Object类型为主。当需要使用这些实例时,必须正确地将该实例转换为原来的类型,否则在运行时将会发生ClassCastException异常。
为了提前预防这种问题,Java提供了泛型机制。其语法如下:
类名<T>
其中,T是泛型的名称,代表某一种类型。开发者在创建该类对象时需要指定T所代表哪种具体的类型。如果不指定具体类型,T则采用Object类型。
【例13.6】创建带泛型的图书类
为Book 图书类创建泛型T,用T声明一个成员变量bookInfo。创创建不同的图书对象,分别将bookInfo的类型指定为字符串、浮点数和布尔值类型。
13.2.3 泛型的常规用法
1.定义泛型类时声明多个类型
在定义泛型类时,可以声明多个类型。语法如下:
class MyClass<T1,T2>{ }
其中,T1和T2为可能被定义的类型。
这样,在实例化指定类型的对象时就可以指定多个类型。例如:
MyClass <Boolean,Float> m = new MyClass<Boolean,Float>();
2.定义泛型类时声明数组类型
定义泛型类时也可以声明数组类型。
【例13.7】定义泛型数组
在项目中创建ArrayClass类,在该类中定义泛型类声明数组类型。
3.集合类声明容器的元素
JDK中的集合接口、集合类都被定义了泛型,其中List<E>的泛型E实际上就是element元素的首字母,Map<K,V>的泛型 K 和 V就是key键和 valu ue 值的首字母。
【例13.8]使用泛型约束集合的元素类型
在项目中创建AnyClass类,在该类中使使用泛型实例化常用集合类。
13.2.4 泛型的高级用法
泛型的高级用法包括限制泛型可用类型和使用类型通配符等。
1.限制泛型可用类型
默认可以使用任何类型来实例化一个泛型类对象,但Java中也对泛型类实例的类型作了限制。语法如下:
class 类名称<T extends anyClass>
其中,anyClass指某个接口或类。
使用泛型限制后,泛型类的类型必须实现或继承anyClass这个接口或类。无论anyClass是接口还是类,在进行泛型限制时都必须使用extends 关键字。
【例13.9】限制泛型的类型必须为List的勺子类
在项目中创建LimitClass类,在该类中限制泛型类型。
2.使用类型通配符
在泛型机制中,提供了类型通配符,其主要作用是在创建一个泛型类对象时限制这个泛型类的类型实现或继承某个接口或类的子类。要声明这样一个对象可以使用“?”通配符来表示,同时使用extends关键字来对泛型加以限制。使用泛型类型通配符的语法如下:
泛型类名称<? extends List>a=null;
其中,<?extends Lis>表示类型未知,当需要使用该泛型对象时,可以单独实例化。例如:
A<? extends List> a = null;
a = new A<ArrayList>();
a = new A<LinkedList>();
如果实例化没有实现List接口的泛型对象,编译将会报错。例如,实例化HashMap对象时,编译器将会报错,因为HashMap类没有实现List接口。
除了可以实例化一个限制泛型类型的实例,还可以将该实例放置在方法的参数中。例如:
public void doSomething(A<? extends List> a){ }
在上述代码中,定义方式有效地限制了传入doSomething)方法的参数类型。
3. 继承泛型类与实现泛型接口
定义为泛型的类和接口也可以被继承与实现。例如,让SubClass类继承ExtendClass 的泛型,代码如下:
class ExtendClass<T1>{}
class SubClass<T1,T2,T3> extends ExtendCla: ss<T1>{}
如果在SubClass类继承ExtendClass类时保留父类的泛型类型,需要在继承时指明,如果没有指明,直接使用“extends ExtendsClass”语句进行继承操作,则SubClass类中的T1、T2和T3 都会自动变为 Object类型,所以在一般情况下都将父类的泛型类型保留。
定义为泛型的接口也可以被实现。例如,i让 SubClass类实现SomeInterface接口,并继承接口的泛型,代码如下:
interface Somelnterface<T1>{}
class SubClass<T1,T2,T3> implements Somelnt terface<T1>{}