JavaSE-泛型机制

泛型是java编程中经常使用,且很有用的一个特性,经常在集合与DAO(数据库访问对象)编程中使用。

1、为什么要使用泛型:

在没有使用泛型时,集合存储和提取数据经常出现两个不足:

1.存储数据时,不能保证数据的类型安全,也就是说,同一集合中可以存储多种类型的数据

2.在集合中获取数据时,必须经过强制转换,才能转换为自己需要类型的数据,而且由于第一条的不足,导致会出现类型转换异常

这就相当于绕了一个弯,把原本的数据类型--->转换为Object类型存储--->转换为原本的数据类型以便使用。

代码:
	//在没有使用泛型时:
	//1、不能保证存储数据类型的安全
	//2、数据需要强转,可能出现ClassCastExcepetion异常
	@Test
	public void testNoGeneric(){
		List list = new ArrayList();
		list.add(88);
		list.add(90);
		list.add(99);
		
		list.add("AA");//不能保证存储数据类型安全
		
		for(int i=0;i

java 的泛型机制就是解决这么样的问题,通过使用泛型:

1.保证数据存储类型的安全,就是通过指定泛型的类型,使得在存储数据时,只能存储先前指定类型

2.避免了数据类型的转换,也就避免了异常

代码:
	//使用泛型,保证数据存储的类型安全
	//同时,避免了强制类型转换
	@Test
	public void testGeneric1(){
		//在指定泛型之后,该泛型类的所有有关泛型的方法或返回类型,都变成了指定的泛型类型
		List list = 
				new ArrayList();//实现类的泛型可以不再指定,只需一个<>即可
		list.add(88);
		list.add(90);
		list.add(99);
		//list.add("AA");//编译时就会报错,保证数据存储类型的安全
		
		//List容器的遍历方法
		Iterator it = list.iterator();
		while(it.hasNext()){
			Integer score = it.next();
			System.out.println(score);
		}
		//或者
//		for(Integer i:list){
//			System.out.println(i);
//		}
	}

2、在集合中使用泛型:

其实通过指定泛型,泛型类的所有与泛型相关的方法都自动变成了指定的类型

集合List:见“为什么使用泛型”一节

集合Map:
	@Test
	public void test3(){
		Map map = 
				new HashMap();//OR new HashMap<>()即可
		map.put("AA", 78);
		map.put("BB", 88);
		map.put("CC", 99);
		
		Set> set = map.entrySet();
		for(Entry o:set){
			System.out.println(o);
		}
	}

3、自定义泛型类、泛型接口和泛型方法

自定义泛型类

通过添加在类型名后边:class ClassName

自定义泛型类Order,

代码:

public class Order {


	private String orderName;
	private int orderId;
	private T t;
	public List list = new ArrayList<>();
	
	public void add(){
		list.add(t);
	}


 	public String getOrderName() {
		return orderName;
	}
	public void setOrderName(String orderName) {
		this.orderName = orderName;
	}
	public int getOrderId() {
		return orderId;
	}
	public void setOrderId(int orderId) {
		this.orderId = orderId;
	}
	public T getT() {
		return t;
	}
	public void setT(T t) {
		this.t = t;
	}
	@Override
	public String toString() {
		return "Order [orderName=" + orderName + ", orderId=" + orderId + ", t=" + t + "]";
	}
	
	//泛型方法,泛型方法的类型与泛型类无关
	public  E getE(E e){
		return e;
	}
	//泛型方法,实现数组到集合的复制
	public  List fromArrayToList(E[] arr, List list){
		for(E e:arr){//数组也可以这样遍历
			list.add(e);
		}
		return list;
	}
}

Order的使用,

代码:

	//自定义泛型类的使用
	@Test
	public void test4(){
		Order order = new Order();
		order.setT(true);
		Boolean b = order.getT();
		System.out.println(b);
		
		order.add();
		List list = order.list;
		System.out.println(list);
		
	}

1.在实例化泛型类对象时,指定泛型的类型,对应的类中所有使用泛型的位置,都变为实例化中指定的泛型的类型

2.如果我们定义了泛型类,但在实例化中未指定泛型类型,那么默认是Object类型


子类继承泛型类,可以指定泛型类型,也可以保持子类继续是泛型类:

代码:

//定义泛型类的子类时,可以指明泛型类型,通过在父类后指定类型
class subOrder extends Order{
}
//也可以不指定,子类也还是泛型,子类父类都需指定泛型
class subOrder2 extends Order{
	
}


泛型接口同泛型类;


泛型方法

泛型方法其实与泛型类无关,泛型方法可以在泛型类中,也可以在非泛型类中。

泛型方法通过权限修饰符后添加泛型,使函数方法成为泛型方法,参看Order类中两个泛型方法getE()和fromArrayToList()

	//泛型方法,泛型方法的类型与泛型类无关
	public  E getE(E e){
		return e;
	}
	//泛型方法,实现数组到集合的复制
	public  List fromArrayToList(E[] arr, List list){
		for(E e:arr){//数组也可以这样遍历
			list.add(e);
		}
		return list;
	}

泛型方法的使用:(其实通过参数,便指定类泛型方法的泛型类型)
代码:
	//泛型方法的使用
	@Test
	public void test5(){
		Order order = new Order();
		//调用泛型方法
		Integer i = order.getE(50);//指定参数,返回类型确定为Integer
		Double d = order.getE(4.3);//返回类型为Double
		
		Integer[] ints = new Integer[]{1,2,3};
		List list = new ArrayList<>();
		List list3 = order.fromArrayToList(ints, list);
		System.out.println(list3);
	}

4、泛型与继承的关系

类之间存在继承关系,String类是Object类的子类,那么泛型中List与List存在怎么样的关系呢?

List与List是并列关系,不存在继承关系:

	/*
	 * List与List不存在子父类关系,属于并列关系
	 * 他们有共同的父类:List
	 */
	@Test
	public void test6(){
		Object obj = null;
		String str = "AA";
		obj = str;//没问题,子类赋给父类
		
		Object[] objs = null;
		String[] strs = new String[]{"AA","BB","CC"};
		objs = strs;//没问题,子类数组赋给父类数组
		
		List listObject=null;
		List listString = new ArrayList();
		listObject = listString;//编译不通过,说明List与List不存在子父类关系

	}其实,List与List 有共同的父类:List ,这就是通配符?的作用,看下节。 
  

5、通配符

通配符,就是应对泛型类之间的继承关系的,分三种:

List 是所有List的父类

代码:

	/**
	 * 通配符?
	 * List 是所有List、List ... 的父类
	 */
	public void test7(){
		
		List list=null;//共同的父类,通配符?
		List listObject = new ArrayList();
		List listString = new ArrayList();
		list = listObject;
		list = listString;
		
		show(listObject);
		show(listString);
	} 
  

List是List 的父类

List 是List 的父类

	/*
	 * List是所有A类及其子类的父类,即:
	 * 	     可以存放A的所有子类及其本身ClassName类;
	 * List 是所有B类及其父类的父类,即:
	 *    可以存放所有B的父类及B类;
	 */
	public void test8(){
		List list1 = null;
		List list2= null;
		list1 = list2;//可以,因为Integer是Number的子类
		
		List list3 = null;
//		list1 = list3;//编译不通过,因为Object 是Number的父类
		
		List list4 = null;
		list4 = list3;//可以
	} 
  

对于通配符的泛型集合List ,是可以进行遍历查看操作的,但是不允许进行写入,只可以写入null

	@Test
	public void test9(){
		List list = new ArrayList<>();
		list.add("AA");
		list.add("BB");
		list.add("CC");
		
		
		List list1= list;
		//可以查看遍历声明通配符List中的元素
		Iterator it = list1.iterator();
		while(it.hasNext()){
			System.out.println(it.next());
		}
		
		//不可以向声明为通配符的泛型List中添加元素,唯一例外的是null
//		list1.add("AA");//编译不通过
		list1.add(null);
	}

6、其他注意点

1.静态方法中不能使用类的泛型

在泛型类中,静态方法中不能使用泛型,因为静态方法在类加载时就加载了,此时还未指定泛型类的类型,不能使用。

但是在静态方法中可以使用泛型方法,即结合静态方法与泛型方法:

	public static  E test(E e){
		return e;
	}

2.如果泛型类是一个接口或者抽象类,则不可创建泛型类的对象

接口和抽象类本身就不可以创建类对象,跟泛型无多大关系

3.不能在catch语句中使用泛型

在泛型类中,可能会写到try-catch语句,进行异常的处理,但是泛型T是不可以在catch语句中使用的。
不能在catch语句中使用,是指catch(T t)是编译不通过的:
	public void test(){
		try{
			
		}catch(T e){//编译错误
			
		}
	}
但是这样使用是可以的:
	public void test(){
		try{
			T t;
		}catch(Exception e){
			T t;
		}
	}







你可能感兴趣的:(JavaSE)