第十三章总结

枚举类型与泛型

枚举类型可以取代以往常量的定义方式,即将常量封装在类或接口中。此外,它还提供了安全检查功能。枚举类型本质上还是以类的形式存在的。泛型的出现不仅可以让程序员少写一些代码,更重要的是它可以解决类型安全问题。泛型提供了编译的安全检查,不会因为将对象置于某个容器中而失去其类型。

一、枚举类型

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代码

interface SeasonInterface{			//四季接口
	int SPRING=1,SUMMER=2,AUTUMN=3,WINTER=4;
}
enum SeasonEnum{					//四季枚举
	SPRING,SUMMER,AUTUMN,WINTER
}
public class SeasonDemo {

	public static void printSeason1(int season) {
		switch(season) {
		case SeasonInterface.SPRING:
			System.out.println("这是春季");break;
		case SeasonInterface.SUMMER:
			System.out.println("这是夏季");break;
		case SeasonInterface.AUTUMN:
			System.out.println("这是秋季");break;
		case SeasonInterface.WINTER:
			System.out.println("这是冬季");break;
		default:
		System.out.println("这不是四季的常量值");
		}

	}
	public static void printSeason2(SeasonEnum season) {
		switch(season) {
		case SPRING:
			System.out.println("这是春季");break;
		case SUMMER:
			System.out.println("这是夏季");break;
		case AUTUMN:
			System.out.println("这是秋季");break;
		case WINTER:
			System.out.println("这是冬季");break;
		}
}
	public static void main(String[] args) {
		printSeason1(SeasonInterface.SPRING);		//使用接口常量做参数
		printSeason1(3);							//可以使用数字做出参数
		printSeason1(-1);							//使用接口常量值意外的数字“冒充”常量
		printSeason2(SeasonEnum.WINTER);			//使用枚举做参数,只能用枚举中有的值,无法“冒充”
	}
	}

运行结果如下:

2、深入了解枚举类型

枚举类型较传统定义常量的方式,除具有参数类型检测的优势外,还具有其他方面的优势。用户可以将一个枚举类型看作是一个类,它继承与java.lang.Enum类,当定义一个枚举类型时,每一个枚举类型都可以看作是枚举类型的一个实例,这些枚举类型成员都被默认为final、public、static修饰,所以当使用枚举类型成员时直接使用枚举类型名称调用枚举类型成员即可。由于枚举类型对象继承了java.lang.Enum类,所以该类中一些操作枚举类型的方法都可以应用到枚举类型中。

表13.1  枚举类型的常用方法

2.1 values()方法

枚举类型实例包含一个values()方法,该方法将枚举中所有的枚举值以数组的形式返回。

举例13.2代码:

enum SeasonEnum{					//四季枚举
	SPRING,SUMMER,AUTUMN,WINTER
}
public class ShowEnum {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		SeasonEnum es[]=SeasonEnum.values();
		for(int i=0;i<es.length;i++) {
			System.out.println("枚举常量:"+es[i]);
	}
	}
}

运行结果如下:

2.2 valueOf()方法与CompareTo()方法

枚举类型中静态方法valueOf()可以将普通字符串转换为枚举类型,而CompareTo()方法用于比较两个枚举类型对象定义时的顺序。

举例13.3代码:

enum SeasonEnum{					//四季枚举
	SPRING,SUMMER,AUTUMN,WINTER
}
public class EnumMethodTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		SeasonEnum tmp=SeasonEnum.valueOf("SUMMER");	//根据字符串创建一个枚举值
		SeasonEnum es[]=SeasonEnum.values();			//获取所有枚举值
		for(int i=0;i<es.length;i++) {
			String message="";							//待输出的消息
			int result=tmp.compareTo(es[i]);			//记录两个枚举的比较结果
			if (result<0) {
				message=tmp+"在"+es[i]+"的前"+(-result)+"个位置";
			}else if(result>0) {
				message=tmp+"在"+es[i]+"的后"+result+"个位置";
			}else if(result==0) {
				message=tmp+"与"+es[i]+"是同一个值";
			}
			System.out.println(message);
		}
			
	}

}

运行结果如下:

2.3 ordinal()方法

枚举类型中的ordinal()方法用于获取某个枚举对象的位置索引值。

举例13.4代码:

enum SeasonEnum{			//四季枚举
	SPRING,SUMMER,AUTUMN,WINTER
}

public class EnumIndexTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		SeasonEnum es[]=SeasonEnum.values();
		for(int i=0;i<es.length;i++) {
			System.out.println(es[i]+"在枚举类型中位置索引值"+es[i].ordinal());
		}
	}

}

运行结果如下:

2.4 枚举类型的构造方法

在枚举类型中,可以添加构造方法,但是规定这个构造方法必须被private修饰符所修饰。枚举类型定义的构造方法语法如下:

enum 枚举类型名称{
    Constants_A("我是枚举成员A"),
    Constants_B("我是枚举成员B"),
    Constants_C("我是枚举成员C"),
    Constants_D("3"),
    private String description;
    private Constants2(){            //定义默认构造方法
    }
    private Constants2(String description){       //定义带参数的构造方法,参数类型为字符串类型
        this.description = description;
    }
    private Constants2(int i){                //定义带参数的构造方法,参数类型为整型
        this.i=this.i+i;
    }
}

无论是有无参构造方法还是有参构造方法,修饰权限都为private.

举例13.5代码:

enum SeasonEnum{			//四季枚举
	SPRING("万物复苏"),
	SUMMER("烈日炎炎"),
	AUTUMN("秋草枯黄"),
	WINTER("白雪皑皑");
	
	private String remarks;						//枚举的备注
	private SeasonEnum(String remarks) {		//构造方法
		this.remarks="我是"+this.toString()+",我来之后"+remarks+"。";
	}
	String getRemarks() {				//获取备注
		return remarks;
	}
}
public class EnumConstructTest {
 
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		SeasonEnum es[]=SeasonEnum.values();
		for(int i=0;i<es.length;i++) {
			System.out.println(es[i].getRemarks());
		}
	}
}

运行结果如下:

3 、使用枚举类型的优势 

特点:

  1. 类型安全。
  2. 紧凑有效的数据定义。
  3. 可以和程序其他部分完美交互。
  4. 运行效率高。

二、泛型

泛型实质上就是使程序员定义安全的类型。在没有出现泛型之前,Java也提供了对object类型的引用“任意化” 操作,这种“任意化”操作就是对object类型引用进行向下转型及向下转型操作,但某些强制类型转换的错误也许不会被编译器捕捉,而在运行后出现异常,可见强制类型转换存在安全隐患,所以在此提供了泛型机制。

1、回顾向上转型与向下转型

在介绍泛型之前,先来看一个例子,在项目中创建Test类,在该类中使基本类型向上转型为object类型。

 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) {
		Test t = new Test();
		t.setB(Boolean.valueOf(true));   //向上转型操作
		System.out.println(t.getB());
		t.setB(Float.valueOf("12.3"));   //向下转型操作
		Float f = (Float)t.getB();
		System.out.println(f);
	}
}

运行结果如下:

在本实例中,Test类中定义了私有的成员变量b,它的类型为Object类型,同时为其定义了相应的setXXX()与getXXX()方法。在类的主方法中,将Boolean.valueOf(true)作为setB()方法的参数,向下转型会出现错误,语法错误没有出现编译器会接受此段代码,,但执行时会出现ClassCastException异常,而泛型机制就有效解决了这一问题。

举例代码如下:

 
	t.setB(Float.valuesOf("12.3"));
	Integer f = (Integer)t.getB();
	System.out.println(f);
2、定义泛型类

Object类为最上层的父类,为了提前预防发生异常,Java提供了泛型机制其语法如下

类名<T>

举例13.6代码:

public class Book<T> {  //定义带泛型的Book<T>类
	private T bookInfo;   //类型形参:书籍信息
	public Book(T bookInfo) {    //参数为类型形参的构造方法
		this.bookInfo = bookInfo;   //为书记信息赋值
	}
	public T getBookInfo() {   //获取书籍信息的值
		return bookInfo;
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//创建参数为String类型的书名对象
		Book<String>bookName =new Book<String>("《Java从入门到精通》");
		//创建参数为String类型的作者对象
		Book<String>bookAuthor =new Book<String>("明日科技");
		//创建参数为String类型的价格对象
		Book<String>bookPrice =new Book<String>("69.8");
		//创建参数为Boolean类型的附赠源码
		Book<Boolean>hasSource =new Book<Boolean>(true);
		//控制台输出书名、作者、价格和是否附赠光盘
		System.out.println("书名:"+bookName.getBookInfo());
		System.out.println("作者:"+bookAuthor.getBookInfo());
		System.out.println("价格:"+bookPrice.getBookInfo());
		System.out.println("是否附赠源代码?"+hasSource.getBookInfo());
	}
 
}

运行结果如下:

3、泛型的常规用法

1.定义泛型类时声明多个类型。语法如下:

class MyClass<T1,T2>{ }

T1,T2为可能被定义的类型

 在实例化指定类型的对象时就可以指定多个类型,例如:

MyClass<Boolean,Float> m = new MyClass <Boolean,Float>();

2.定义类型类时声明数组类型

定义泛型类时也可以声明数组类型。

举例13.7代码定义泛型数组:

 public class ArrayClass<T> {
	private T[] array;    //定义泛型数组
	public T[] getArray() {
		return array;
	}
	public void setArray(T[] array) {
		this.array =array;
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ArrayClass<String>demo =new ArrayClass<String>();
		String value[] = {"成员1","成员2","成员3","成员4","成员5"};
		demo.setArray(value);
		String array[]=demo.getArray();
		for (int i =0;i<array.length;i++) {
			System.out.println(array[i]);
		}
	}
 
}

运行结果如下:

3.集合类声明容器的元素

JDK中的集合接口、集合类都被定义了泛型,其中List<E>的泛型E实际上就是element元素的首字母,Map<K,V>的泛型K和V就是key键和value值的首字母。常用的被泛型化的集合类如下表:

举例13.8代码:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
 
public class AnyClass {
	public static void main(String[] args) {
		// 定义ArrayList容器,设置容器内的值类型为Integer
		ArrayList<Integer> a = new ArrayList<Integer>();      // 为容器添加新值
		a.add(1); 
		for (int i = 0; i < a.size(); i++) {
			// 根据容器的长度,循环显示容器内的值
			System.out.println("获取ArrayList容器的值:" + a.get(i));
		}
		// 定义HashMap容器,设置容器的键名与键值类型分别为Integer与String型
		Map<Integer, String> m = new HashMap<Integer, String>();
		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));        // 根据键名获取键值
		}
	}
}

运行结果如下:

4 、泛型的高级用法

泛型的高级用法包括限制泛型可用类型和使用类型通配符等。

1.限制泛型可用类型

默认可以使用任何类型来实例化一个泛型类对象,但Java中也对泛型类实例的类型做了限制,语法如下:

class 类名称<T extends anyClass>

其中anyClass指某个接口或类。

使用泛型限制后半泛型类的类型必须实现或者继承anyClass这个接口或类,无论anyclass是接口还是类,在进行泛型限制时都必须使用extends关键字

举例13.9代码:


	import java.util.ArrayList;
	import java.util.HashMap;
	import java.util.LinkedList;
	import java.util.List;
	
	
		public class LimitClass <T extends List>{
			public static void main(String[]args) {
				// 可以实例化已经实现List接口的类
				LimitClass<ArrayList> l1 = new LimitClass<ArrayList>();
				LimitClass<LinkedList> l2 = new LimitClass<LinkedList>();
				// 这句是错误的,因为HashMap没有实现List()接口
				LimitClass<HashMap> l3 = new LimitClass<HashMap>();
				
			}
 
}

运行结果如下:

出现异常:异常为Hasamap不能·被实例化

如果我们想它不报错的话就将hash map改为导入类中的list。但仍然无法运行,只是不报错。如下

 
	import java.util.ArrayList;
	import java.util.HashMap;
	import java.util.LinkedList;
	import java.util.List;
	
	
		public class LimitClass <T extends List>{
			public static void main(String[]args) {
				// 可以实例化已经实现List接口的类
				LimitClass<ArrayList> l1 = new LimitClass<ArrayList>();
				LimitClass<LinkedList> l2 = new LimitClass<LinkedList>();
				// 这句是错误的,因为HashMap没有实现List()接口
				LimitClass<List> l3 = new LimitClass<List>();
				
			}
 
}

2.使用类型通配符

在泛型机制中,提供了类型通配符,其主要作用是在创建一个泛型类对象时,限制这个泛型类的类型,实现或继承某个接口或类的子类。要声明这样一个对象可以使用“?”通配符来表示,同时使用extends。关键字来对泛型加以限制使用泛型类型通配符的语法,如下:

泛型类名称<?extends List> a=null;

其中,<? extends List>表示类型未知,当需要使用该泛型对象时,可以单独实例化,例如:

A <?extends List> a=null;//定了上界和下界

a=new A<ArraryList>();

a=new A<ArraryLinkList>();

像上述例子的HashMap类没有实现List接口,那编译器就会报错.除了可以实例化一个限制泛型类型的实例,还可以将该实例放置在方法的参数

public void do Something(A<? extendsList>a){}

在上述代码中,定义方式有效地限制了传入do Something()方法的参数类型。如果使用A<?>这种形式实例化泛型类对象,则默认表示可以将a指定为实例化Object及以下的子类类型,例如:

List <String>l1 = new ArraryList<String>();    //实例化一个对象

l1.add("成员");                      //在集合中添加内容

List<?>l2=l1;

List<?>l3 = new LinkkedList<integer>();

System.out.println(2.get(0));    //获取集合中第一个值

在上面的例子中list类型的对象可以接受时君类型的和ArraryList集合也可以接受Integer类型的LLinkedList集合。

注意:List<?>12=11和List12=11有区别
使用通配符声明的名称实例化的对象不能对其加入新的信息,只能获取或删除。例如:

l1.set(0,"成员改变");     //没有使用通配符的对象调用set()方法

//l2.set(0,"成员改变");//使用通配符的对象调用set()方法,不能被调用。

//l3.set(0,1);

l2.get(0);  //可以使用l2的获取集合中的值

l2.remove(0);//根据键名删除集合中的值

3.继承泛型类与接口

定义为泛型的类和接口也可以被继承与实现。例如让Subclass类继承Extendclass类的泛型,代码入下:

class Extend Class <T1>{}

Class Sub Class <T1,T2,T3>extends Extend Class <T1>{}

如果在Sub Class类继承Extend Class类时保留父类的泛型类型,需要在继承时期指明。如果没有指明,直接使用extends Extends Class语句进行继承操作,则SubClass类中的 T1T2和T3都会自动变为object类型,所以在一般情况下都将父类的泛型类型保留。

定义为泛型的接口也可以被实现,例如让Sub Class类实现Some Interface接口,并继承接口的泛型。代码如下:

interface SomeInterface<T1>{}

class SubClass<T1,T2,T3>implements SomeInterface<T1>{}
5、 泛型总结

总结一下泛型的使用方法。

  1. 泛型类型参数只能是类类型,不可以是简单类型,比如A< int>这种泛型定义就是错误的。
  2. 泛型的类型个数可以是多个
  3. 可以使用extends关键字限制泛型的类型
  4. 可以使用通配符限制泛型的类型
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值