Java中泛型的使用

Java中泛型的使用

泛型

1.概述

在前面学习集合时,我们知道了集合中是可以存放任意对象的,只要把对象存储到集合后,那么这时他们都会被提升成Object类型。当我们在取出每一个对象,并且进行相应的操作时,必须采用类型转换。比如下面程序:

public static void main(String[] args) {
   
		List list = new ArrayList();
		list.add("abc");
		list.add("def");
		list.add(123);// 由于集合没有做任何限定,任何类型都可以给其中存放
		// 相当于:Object obj=new Integer(5);

		Iterator it = list.iterator();
		while (it.hasNext()) {
   
			// 需要打印每个字符串的长度,就要把迭代出来的对象转成String类型
			String str = (String) it.next();// String str=(String)obj;
			// 编译时期仅检查语法错误,String是Object的儿子可以向下转型
			// 运行时期String str=(String)(new Integer(5)),
			// 由于String与Integer没有父子关系所以转换失败。
			// 程序在运行时发生了问题java.lang.ClassCastException
			System.out.println(str.length());
		}
}

可以发现:这种数据类型间的强制转换,存在安全隐患,所以为了解决这个安全问题,Java中引入了泛型技术。

2.定义

泛型就是指明集合中存储数据的类型。如果没有指明数据类型,那集合可以存储任意类型,就会存在上述提到的类型转换异常的安全隐患。

3.使用

在集合类型后面写上尖括号<>,并加上要存储的数据类型。如:ArrayList<Integer> arr = new ArrayList<Integer>();
示例:

public static void function() {
   
		Collection<String> coll = new ArrayList<String>();//指明集合中存储的数据类型为String
		coll.add("abc");
		coll.add("qwe");
		//coll.add(123);
		Iterator<String> it = coll.iterator();//集合指明为String,所以迭代器取出来的元素也必须为String
		while (it.hasNext()) {
   
			String s = it.next();//此处便不需要数据类型强转了
			System.out.println(s.length());
		}
}

使用泛型后,不用做数据类型的转换,一定程度上精简了代码;并且如果添加的元素不符合所指明的数据类型,代码将会标红报错,无法编译,提高了安全性。

4.补充

java中的泛型,其实是一种伪泛型,它只是一种编译手段,如:ArrayList<String> arr; arr.add("");如果add中的对象是String则编译成功,否则编译失败。但是:在编译后的class文件中,是没有泛型的。所以java中的泛型是假的,编译后的class文件中没有泛型概念。也就是说如果Java文件反编译回源文件是没有泛型的。(好像C++中的泛型是真的 ^ o ^ )

5.带有泛型的类、方法和接口

上文中我们在集合中大量使用到了泛型,但泛型同样可灵活地将数据类型应用到不同的类、方法、接口中。将数据类型作为参数进行传递。

泛型的类:语法修饰符 class 类名 <E> { }。E:Element 元素,实际思想就是一个变量而已。如:ArrayList<Integer>,E接受到的类型就是Integer类型。

泛型的方法:语法修饰符 <E> 返回值类型 方法名(参数){ }。例如,API中的ArrayList集合中的方法:public <E> E[] toArray(E[] a){ }

泛型的接口:语法修饰符 interface 接口名<E>{ }。如,public interface List <E>{abstract boolean add(E e);}

总之在使用时直接将E视为一个未知或任意的数据类型就行了,你传的什么类型,E就变为那个类型。

6.泛型的通配符

直接看示例代码:虽然还没讲到HashSet,但其公共用法都差不多,这里也不需要纠结于其用法,只是为了代码演示。

public class GenericDemo {
   
	public static void main(String[] args) {
   
		ArrayList<String> array = new ArrayList<String>();//存储String类型
		
		HashSet<Integer> set = new HashSet<Integer>();//存储Integer类型
		
		array.add("123");
		array.add("456");
		
		set.add(789);
		set.add(890);
	/* * 这时需要定义一个方法,要求可以同时迭代这2个集合,怎么办呢? * 这时会发现 , 数据类型不确定,可能是String的,也可能是Integer的,参数既不能写ArrayList,也不能写HashSet。 * 那么有没有一种共同实现的接口呢? * 这里需要用到泛型的 通配 ,作用是:匹配所有的数据类型。符号是: ? */
		iterator(array);
		iterator(set);
	}
	
	public static void iterator(Collection<?> coll){
   
		Iterator<?> it = coll.iterator();
		while(it.hasNext()){
   
			//it.next()获取的对象,任意类型。
			System.out.println(it.next());
		}
	}
}

7.泛型的限定

同样直接看示例代码:(要求定义一个方法,可以同时遍历3集合,遍历三个集合的同时,还可以调用对象的方法)

酒店的员工类:

/* * 酒店的员工类 * 员工共性, 姓名,工号 工作方法 */
public abstract class Employee {
   
	private String name;
	private String id;
	
	public Employee(){
   }
	
	public Employee(String name,String id){
   
		this.name = name;
		this.id = id;
	}
	
	public abstract void work();
	
	public String getName() {
   
		return name;
	}
	public void setName(String name) {
   
		this.name = name;
	}
	public String getId() {
   
		return id;
	}
	public void setId(String id) {
   
		this.id = id;
	}
	
}

VIP接口:

/* * 酒店的VIP服务 * 厨师和服务员 */
public interface VIP {
   
	public abstract void services();
}

厨师类:

/* * 定义厨师类 * 属于员工一种,具有额外服务功能 * 继承Employee,实现VIP接口 */
public class ChuShi extends Employee implements VIP{
   
	//空参数构造方法
	public ChuShi(){
   }
	
	//有参数构造方法
	public ChuShi(String name,String id){
   
		super(name,id);
	}
	
	//抽象方法重写
	public void work(){
   
		System.out.println("厨师在炒菜");
	}
	public void services(){
   
		System.out.println("厨师做菜加量");
	}
}

服务员类:

/* * 定义服务员类 * 属于员工一种,具有额外服务功能 * 继承Employee,实现VIP接口 */
public class FuWuYuan extends Employee implements VIP{
   
	//空参数构造方法
	public FuWuYuan() {
   
		super();
		
	}
   //满参数构造方法
	public FuWuYuan(String name, String id) {
   
		super(name, id);
		
	}
	
	//抽象方法重写
	public void work(){
   
		System.out.println("服务员在上菜");
	}

	public void services(){
   
		System.out.println("服务员给顾客倒酒");
	}
}

经理类:

/* * 定义经理类 * 属于员工一种,没有VIP功能 * 自己有奖金属性 */
public class JingLi extends Employee {
   
	//空参数构造方法
	public JingLi(){
   }
	
	//满参数构造方法
	public JingLi(String name,String id,double money){
   
		super(name, id);
		this.money = money;
	}
	
	//定义奖金属性
	private double money;
	
	public double getMoney() {
   
		return money;
	}
	
	public void setMoney(double money) {
   
		this.money = money;
	}

	//重写抽象方法
	public void work(){
   
		System.out.println("管理,谁出错我罚谁");
	}
}

运行主程序类:

/* * 将的酒店员工,厨师,服务员,经理,分别存储到3个集合中 * 定义方法,可以同时遍历3集合,遍历三个集合的同时,可以调用工作方法 */
import java.util.ArrayList;
import java.util.Iterator;
public class GenericTest {
   
	public static void main(String[] args) {
   
		//创建3个集合对象
		ArrayList<ChuShi> cs = new ArrayList<ChuShi>();
		ArrayList<FuWuYuan> fwy = new ArrayList<FuWuYuan>();
		ArrayList<JingLi> jl = new ArrayList<JingLi>();
		
		//每个集合存储自己的元素
		cs.add(new ChuShi("张三", "后厨001"));
		cs.add(new ChuShi("李四", "后厨002"));
		
		fwy.add(new FuWuYuan("翠花", "服务部001"));
		fwy.add(new FuWuYuan("酸菜", "服务部002"));
		
		jl.add(new JingLi("小名", "董事会001", 123456789.32));
		jl.add(new JingLi("小强", "董事会002", 123456789.33));
		
	/* * 要求定义方法,可以同时遍历3集合,遍历三个集合的同时,可以调用工作方法 work * ? 通配符,迭代器it.next()方法取出来的是Object类型,无法调用work方法 * 方法一:强制转换: it.next()=Object o ==> Employee * 方法二:参数控制,可以传递Employee对象,也可以传递Employee的子类的对象, * 这就要用到泛型的限定 本案例中,父类固定Employee,但是子类可以为任意。 * 语法为: < ? extends Employee > 限制的是父类, 上限限定, 可以传递Employee,也可以传递它的子类对象。 */
		iterator(jl);
		iterator(fwy);
		iterator(cs);
	}
	
	public static void iterator(ArrayList<? extends Employee> array){
   
		
		 Iterator<? extends Employee> it = array.iterator();
		 while(it.hasNext()){
   
			 //获取出的next() 数据类型,可以是任意Employee类型
			 Employee e = it.next();
			 e.work();
		 }
	}
}

另外,< ? super Employee >限制的是子类, 下限限定, 可以传递Employee和它的父类对象。注意与< ? extends Employee >区别。

本文来源MrKorbin,由架构君转载发布,观点不代表Java架构师必看的立场,转载请标明来源出处:https://javajgs.com/archives/25325

发表评论