目录
11.1 枚举
11.1.1 使用枚举类型设置常量
11.1.2 深入了解枚举类型
11.1.3 使用枚举类型的优势
11.2 泛型
11.2.1 回顾"向上转型" 与"向下转型"
11.2.2 定义泛型类
11.2.3 泛型的常规用法
11.2.4 泛型的高级用法
11.2.5 泛型总结
11.3 小结
11.1 枚举
JDK1.5中新增了枚举,枚举是一种数据类型,它是一系列具有名称的常量的集合。比如在数学中所学的集合:A={1,2,3},当使用这个集合时,只能使用集合中的1,2,3这3个元素,不是这3个元素的值就无法使用。Java中的枚举也是同样的道理,比如在程序中定义一个性别枚举,里面只有两个值:男、女,那么在使用该枚举时,只能使用男和女这两个值,其他的任何值都是无法使用的
11.1.1 使用枚举类型设置常量
以往设置常量,通常将常量放置在接口中,这样在程序中就可以直接使用,并且该常量不能被修改,因为在接口中定义常量时,该常量的修饰符为final与static.
public interface Constants{
public static final int Constants_A=1;//定义一个泛型变量并赋初值
public static final int Conostants_B=12;
}
在JDK1.5版本中新增枚举类型后就逐渐取代了这种常量定义方式,因为通过使用枚举类型,可以赋予程序在编译时进行检查的功能。语法如下:
public enum Constants{
Constants_A,//泛型常量
Constants_B,
Constants_c
}
其中,enum是定义枚举类型的关键字。当需要在程序中使用该常量时,可以使用Constants.Constant_A来表示。
例11.1
public class ConstantsTest {
enum Constants2 {//将常量放置在枚举类型中
Constants_A ,
Constants_B
}
public static void doit2(Constants2 c) { //定义一个参数对象是枚举类型的方法
switch (c) { //根据 枚举类型对象做不同操作
case Constants_A:
System.out.println("doit2() Constants_A");
break;
case Constants_B:
System.out.println("doit2() Constants_B");
break;
}
}
public static void main(String[] args) {
ConstantsTest.doit2(Constants2.Constants_A);//使用接口中定义的常量
ConstantsTest.doit2(Constants2.Constants_B); //使用枚举类型中的常量
//ConstantsTest.doit2(3);
// TODO Auto-generated method stub
}
}
11.1.2 深入了解枚举类型
1.操作枚举类型成员的方法
枚举类型较传统定义常量的方式,除了具有参数类型检测的优势之外,还具有其他方面的优势。用户可以将一个枚举类型看作是一个类,它继承于java. lang. Enum类,当定义一个枚举类型时,每一个枚举类型成员都可以看作是枚举类型的一个实例,这些枚举类型成员都默认被final、public、static修饰,所以当使用枚举类型成员时直接使用枚举类型名称调用枚举类型成员即可。由于枚举类型对象继承于java. lang. Enum类,所以该类中一些操作枚举类型的方法都可以应用到枚举类型中。
1.values():将枚举类型成员以数组的形式返回。
public class ShowEnum {
enum Constants { // 将常量放置在枚举类型中
Constants_A, Constants_B, Constants_C, Constants_D
}
// 循环由values()方法返回的数组
public static void main(String[] args) {
Constants enumArray[] = Constants.values();//values()方法返回枚举数组
for (int i = 0; i < enumArray.length; i++) {
// 将枚举成员变量打印
System.out.println("枚举类型成员变量:" + enumArray[i]);
}
}
}
2.valueOf()与compareTo()
枚举类型中静态方法valueOft)可以将普通字符串转换为枚举类型,而compareTo(万房)较两个枚举类型成员定义时的顺序。调用compareTo(方法时,如果方法中参数在调用该方法对象位置之前,则返回正整数;如果两个互相比较的枚举成员的位置相同,则返回0;如参数在调用该方法的枚举对象位置之后,则返回负整数.
例11.3
enum Constants { //将常量放在枚举类型中
Constants_A,
Constants_B,
Constants_C,
Constants_D
}
public class EnumMethodTest { //定义比较枚举类型方法,参数类型为枚举类型
public static void compare(Constants c) {
Constants array[] = Constants.values();//根据values()方法返回的数组 //将比较结果返回
for (int i =0;i < array.length;i++) {
System.out.println(c + "与" + array[i] + "的比较结果为:" + c.compareTo(array[i]));
}
} //在主方法中调用compare()方法
public static void main(String[] args) {
compare(Constants.valueOf("Constants_B"));
// TODO Auto-generated method stub
}
}
3.ordinal():
获取枚举对象的位置索引值。
例11.4
public class EnumIndexTest {
enum Constats2 { //将常量放在枚举类型中
Constants_A,
Constants_B,
Constants_C
}
public static void main(String[] args) {
Constants2[] arrayCon = Constants2.values();
for (int i =0;i
2.枚举类型中的构造方法.
在枚举类型中,可以添加构造方法,但是规定这个构造方法必须为private修饰符或者默认修饰符所修饰.
public enum Constants2 {
Constants_A("我是枚举成员A"),
Constants_B("我是枚举成员B"),
Constants_C("我是枚举成员C"),
Constants_D(3);String description;
int i;
private Constants2() { //定义默认构造方法
}
//定义带参数的构造方法,参数类型为字符串型
private Constants2 (String description) {
this.description=description;
}
private Constants2(int i) { //定义带参数的构造方法,参数类型为整型
this.i=this.i+1;
}
}
例11.5
public class EnumConTest {
enum Constants2 { //将常量放置在枚举类型中
Constants_A("我是枚举成员A"), //定义带参数的枚举类型成员
Constants_B("我是枚举成员B"),
Constants_C("我是枚举成员C"),
Constants_D(3);
private String description;
private int i =4;
//定义参数为String型的构造方法
private Constants2(String description) {
this.description = description;
}
private Constants2(int i) { //定义参数为int型的构造方法
this.i = this.i + i;
}
public String getDescription() { //获取description的值
return description;
}
public int getI() { //获取i的值
return i ;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Constants2 array[] = Constants2.values(); //获取枚举成员数组
for(int i = 0;i
interface EnumInterface {
public String getDescription();
public int getI();
}
public enum AnyEnum implements EnumInterface {
Constants_A { // 可以在枚举类型成员内部设置方法
public String getDescription() {
return ("我是枚举成员A");
}
public int getI() {
return i;
}
},
Constants_B {
public String getDescription() {
return ("我是枚举成员B");
}
public int getI() {
return i;
}
},
Constants_C {
public String getDescription() {
return ("我是枚举成员C");
}
public int getI() {
return i;
}
},
Constants_D {
public String getDescription() {
return ("我是枚举成员D");
}
public int getI() {
return i;
}
};
private static int i = 5;
public static void main(String[] args) {
AnyEnum array[] = AnyEnum.values();
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]+ "调用getDescription()方法为:" +array[i].getDescription());
System.out.println(array[i] + "调用getI()方法为:" +array[i].getI());
}
}
}
11.1.3 使用枚举类型的优势
(1)类型安全.
(2)紧凑有效的数据定义.
(3)可以和程序其他部分完美交互.
(4)运行效率高.
11.2 泛型
11.2.1 回顾"向上转型"与"向下转型"
例11.7
public class Test {
private Object b; //定义Object类型成员变量
public Object getB() { //设置相应的getxxx()方法
return b;
}
public void setB(Object b) { //设置相应的setxxx()方法
this.b = b;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Test t = new Test();
t.setB(new Boolean(true)); //向上转型操作
System.out.println(t.getB());
t.setB(new Float(12.3));
Float f = (Float) (t.getB()); //向下转型操作
System.out.println(f);
}
}
在本实例中,Test类中定义了私有的成员变量b 它的类型为Object类型,同时为其定义了相应
的setXXX0与getXXX0方法。在类主方法中,将nev wBoolean(true)对象作为setBO方法的参数,由于 setB0方法的参数类型为Object,这样就实现了“向上转型”操作。同时在调用getB0方法时,将 getBO方法返回的 Object 对象以相应的类型返回,这个就是“向下转型”操作,问题通常就会出现在这里。因为"向上转型"是安全的,而如果进行"向下转型"操作时用错了类型,或者并没有执行该操作,就会出现异常.例如以下代码:
t.setB(new Float(12.3));
Integer f=(Integer) (t.getB());
System.out.println(f);
改代码并不存在语法错误,可以被编译器接受,但在执行时会出现ClassCastException异常.这样看来."向下转型"操作通常会出现问题,而泛型机制有效地解决了这一问题.
11.2.2 定义泛型类
Object类为最上层的父类,很多程序员为了使程序更为通用,设计程序时通常使传入的值与返回的值都以Object类型为主。当需要使用这些实例时,必须正确地将该实例转换为原来的类型,否则在运行时将会发生ClassCastException异常。
泛型机制,语法如下
类名
(T表示一个类型名称)
public class OverClass { //定义泛型类
private T over; // 定义泛型成员变量
public T getOver() { //设置getXXX()方法
return over;
}
public void setOver(T over) { //设置getXXX()方法
this.over = over;
}
public static void main(String[] args) {
//实例化一个Boolean型对象
OverClass Over1 = new OverClass();
//实例化一个Float型对象
OverClass Over1 = new OverClass();
Over1.setOver(true);
Over1.setOver(12.3f);
Boolean b = over1.getOver(); //不需要进行类型转换
Float f = over2.getOver();
System.out.println(b);
System.out.println(f);
注:在定义泛型类时,一般类型名称使用T来表示,而容器的元素使用E来表达.
1.定义泛型类时声明多个类型
在定义泛型类时,可以声明多个类型。
Class
Class:泛型类名称
2.定义泛型类时声明数组类型
public class ArrayClass {
private T[] array; // 定义泛型数组
public T[] getArray() { // 获取成员数组
return array;
}
public void SetArray(T[] array) { // 设置SetXXX()方法为成员数组赋值
this.array = array;
}
public static void main(String[] args) {
ArrayClass a = new ArrayClass();
String[] array = { "成员1", "成员2", "成员3", "成员4", "成员5" };
a.SetArray(array); // 调用SetT()方法
for (int i = 0; i < a.getArray().length; i++) {
System.out.println(a.getArray()[i]); // 调用getT()方法返回数组中的值
}
}
}
3.集合类声明容器的元素
import java.util.HashMap;
import java.util.Map;
public class MutiOverClass {
public Map m = new HashMap(); // 定义一个集合HashMap实例
// 设置put()方法,将对应的键值与键名存入集合对象中
public void put(K k, V v) {
m.put(k, v);
}
public V get(K k) { // 根据键名获取键值
return m.get(k);
}
public static void main(String[] args) {
// 实例化泛型类对象
MutiOverClass mu = new MutiOverClass();
for (int i = 0; i < 5; i++) {
// 根据集合的长度循环将键名与具体值放入集合中
mu.put(i, "我是集合成员" + i);
}
for (int i = 0; i < mu.m.size(); i++) {
// 调用get()方法获取集合中的值
System.out.println(mu.get(i));
}
}
}
其实在例11.9中定义的泛型类 MutiOverClass 纯属多余,因为在Java中这些集合框架已经都被泛发化了,可以在主方法中直接使用public Map
import java.util.*;
public class ListClass {
public static void main(String[] args) {
// 定义ArrayList容器,设置容器内的值类型为Integer
List a = new ArrayList();
a.add(1); // 为容器添加新值
for (int i = 0; i < a.size(); i++) {
// 根据容器的长度循环显示容器内的值
System.out.println("获取ArrayList容器的值:" + a.get(i));
}
// 定义HashMap容器,设置容器的键名与键值类型分别为Integer与String型
Map m = new HashMap();
for (int i = 0; i < 5; i++) {
m.put(i, "成员" + i); // 为容器填充键名与键值
}
for (int i = 0; i < m.size(); i++) {
// 根据键名获取键值
System.out.println("获取Map容器的值" + m.get(i));
}
// 定义Set容器,使容器中的内容为String型
Set Set= new HashSet<>();
Set.add('一');
Set.add('二');
for(Character c : Set) {
System.out.println("获取Set容器的值 " + c );
}
}
}
11.2.4 泛型的高级用法
泛型的高级用法主要包括通过类型参数T的继承和通过类型统配符的继承来限制泛型类型.
1.通过类型参数T的继承限制泛型类型
默认可以使用任何类型来实例化一个泛型类对象,但Java中也对泛型类实例的类型作了限制,这主要通过对类型参数T实现继承来体现。
class 类名称
anyClass:接口或者类
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class Limitclass {//限制泛型的类型
public static void main (string[] args) {//可以实例化已经实现List接口的类
Limitclass 11 = new LimitClass();
Limitclass12 = new Limitclass();
//这句是错误的,因为HashMap没有实List()接口
//LimitClass 13=new Limitclass();
上面代码中,将泛型作了限制,设置泛型类型必须实现List接口。例如,ArrayList和 LinkedList 都实现了List接口,而HashMap没有实现List接口,所以在这里不能实例化 HashMap类型的泛型对象。
当没有使用extends关键字限制泛型类型时,默认 Object类下的所有子类都可以实例化泛型类对象。如图11.11所示的两个语句是等价的。
2.通过类型通配符的继承限制泛型类型
在泛型机制中,提供了类型通配符,其主要作用是在创建一个泛型类对象时,限制这个泛型类的类型,或者限制这个泛型类型必须继承某个接口或某个类(或其子类)。要声明这样一个对象可以使用“?”通配符,同时使用extends 关键字来对泛型加以限制。
说明:
通过对类型参数T实现继承限制泛型类型时,在声明时就进行了限制,而通过对类型通配符实现继承限制泛型类型时,则在实例化时才进行限制。
使用泛型类型通配符的语法如下:
泛型类名称 a=null;
其中, extends List>表示类型未知,当需要使用该泛型对象时,可以单独实例化。例如,在项目中创建一个类文件,在该类中限制泛型类型。
A a=null;
a=new A();
a=new A();
如果实例化没有实现List 接口的泛型对象,编译器将会报错。例如,实例化HashMap对象时,编译器将会报错,因为 HashMap类没有实现List 接口。
除了可以实例化一个限制泛型类型的实例之外,还可以将该实例放置在方法的参数中。例如,在项目中创建一个类文件,在该类的方法参数中使用匹配字符串。
public void doSomething(A extends List> a){
}
在上述代码中,定义方式有效地限制了传入doSomething()方法的参数类型。
如果使用A>这种形式实例化泛型类对象,则默认表示可以将A指定为实例化Object 及以下的子类类型。读者可能对这种编码类型有些疑惑,下面的代码将直观地介绍A>泛型机制。
例11.11
import java.util.*;
public class WildClass {
public static void main(String[] args) {
// TODO Auto-generated method stub
List l1 = new ArrayList(); //创建一个ArrayList对象
l1.add("成员"); //在集合中添加内容
List> l2 = l1; //使用通配符
List> l3 = new LinkedList();
System.out.println("l1:" +l1.get(0)); //获取l1集合中第一个值
System.out.println("l2:" +l2.get(0)); //获取l2集合中第一个值
l1.set(0, "成员改变"); //使用通配符的对象不能调用set()方法
//l2.add("添加"); //使用通配符的对象不能调用add方法
// l2.set(0,"成员改变"); //使用通配符的对象不能调用set方法
//13.add(1);
//13.set(0,1);
System.out.println("l1:" +l1.get(0));
}
}
3.继承泛型类与实现泛型接口
定义为泛型的类和接口也可以被继承与实现。
例如,在项目中创建一个类文件,在该类中继承泛型类。
public class Extendclass
{
}
class Subclassextends ExtendClass {//泛型可以比父类多,但不可以比父
类少
}
如果在SubClass类继承ExtendClass类时保留父类的泛型类型,需要在继承时指明,如果没有指明,直接使用extends ExtendsClass语句进行继承操作,则SubClass类中的T1、T2和T3都会自动变为Object,所以在一般情况下都将父类的泛型类型保留。
定义的泛型接口也可以被实现。
例如,在项目中创建一个类文件,在该类中实现泛型接口。
interface TestInterface
{
}
class SubClass2implements TestInterface {
}
11.2.5泛型总结
使用泛型需遵循以下原则。
(1 )泛型的类型参数只能是类类型,不可以是简单类型,如A
(2)泛型的类型个数可以是多个.
(3 )可以使用extends关键字限制泛型的类型。
( 4)可以使用通配符限制泛型的类型。