智渔课堂官方免费教程三十:Java基础教程之泛型

时间:2022-01-17 16:17:36

泛型

先给大家举个例子;如现在有一家工厂,可以生产手机,也可以生产电脑。以后还可能生产其他产品。
如果给某个工厂加上了泛型,就规定了这个工厂只能生产手机或电脑,不能再生产其他产品了。
实例:package generic;
/**
* 产品枚举Product
* @author 学霸联盟 - 赵灿
*/
public enum Product {
手机,电脑
}

package generic;
/**
* 手机类Phone
* @author 学霸联盟 - 赵灿
*/
public class Phone {
/**
* 拨打电话的方法call
*/
public void call(){
System.out.println("拨打电话");
}
}

package generic;
/**
* 电脑类Computer
* @author 学霸联盟 - 赵灿
*/
public class Computer {
/**
* 编写代码的方法
*/
public void coding() {
System.out.println("编写代码");
}
}
package generic;/** * 普通工厂类Factory:用于和泛型工厂类做对比 * @author 学霸联盟 - 赵灿 */public class Factory {	/**	 * 创建返回值类型为Object[],功能是生产多个产品对象	 * 由于存在多种产品类型,所以返回值取所有产品共同的父类型Objec的数组	 */	public Object[] getProduct(){		// 创建长度为2的Object类型的一维数组,可以存储手机,也可以存放电脑		Object[] products = new Object[2];		// 创建手机对象,存入数组下标为0的位置		products[0] = new Phone();		// 创建电脑对象,存入数组下标为1的位置		products[1] = new Computer();		// 返回数组products		return products;	}		/**	 * 创建根据参数值获取相应的产品	 * 如果以后需要使用该方法创建其他产品,还要在switch语句中增加case语句,拓展性差	 */	public Object[] getProduct(Product productType){		// 创建长度为2的Object类型的一维数组,可以存储手机,也可以存放电脑		Object[] products = new Object[2];		switch (productType) {		case 手机:			// 创建手机对象,存入数组下标为0的位置			products[0] = new Phone();			/*			 * 即使productType的值为手机,此处依然可以存入电脑对象			 * 如果此处误创建成Computer对象,获取时极有可能被误转为Phone类型			 * 从而导致类型转换异常,健壮性(安全性)差			 */			products[1] = new Computer();			break;		case 电脑:			// 创建两个Computer对象存入数组			products[0] = new Computer();			products[1] = new Computer();			break;		}		// 返回数组products		return products;	}}
package generic;import java.lang.reflect.Array;/** * 泛型工厂类GenericFactory<T>:用于演示泛型 * 其中T是类型GenericFactory类的形参 * 使用类型GenericFactory可以传入一个类型(类名)作为实参 * 在类型GenericFactory的内部便可以将T作为数据类型使用 * 例如:GenericFactory<String>这样编译后类中所有的T都会被替换为String *  * T在这里一般称作占位符,也可以是其他的任意符合标识符命名规则的符号 * 常用的为T和E,T:type(类型)首字母;E:element(元素)首字母 *  * 如果需要使用多个泛型,占位符中间使用英文格式的逗号隔开 * 例如:GenericFactory<T,E> * 使用时也是按照位置顺序对应传入 * 例如:GenericFactory<String,Integer> * 其类中所有的T都会被替换成String,所有的E都会被替换成Integer *  * @author 学霸联盟 - 赵灿 */public class GenericFactory<T> {	/**	 * 声明一个返回值类型为T[](T类型的数组),参数为Class类型对象的方法	 * 该方法用于获取工厂的产品	 */	public T[] getProduct(Class<T> cls){		//创建一个长度为2,T类型的数组t;初学的同学知道以下代码的作用即可		T[] t = (T[]) Array.newInstance(cls, 2);		try {			//创建类型T的对象;等价于 t = new T();			t[0] = cls.newInstance();			t[1] = cls.newInstance();		} catch (InstantiationException e) {			e.printStackTrace();		} catch (IllegalAccessException e) {			e.printStackTrace();		}		//返回对象t		return t;	}}
package generic;/** * 泛型测试类GenericTest * 用于测试和对比两种工厂的使用 * @author 学霸联盟 - 赵灿 */public class GenericTest {	public static void main(String[] args) {		//创建普通工厂对象f		Factory f = new Factory();		//通过工厂f的getProduct方法获取的产品数组是Object类型的数组//所以这个数组中可能存储着多种产品		Object[] products = f.getProduct(Product.手机);		//当从数组中取出产品时是Object类型的,需要强制类型转换		Phone fp = (Phone)products[0];		//使用手机对象fp调用拨打电话的方法call		fp.call();		/*		 * 我们知道此时下标为1处的产品是电脑		 * 如果使用者不知道产品数组中存储的是什么类型的产品时,依然将其强制转换成手机类型		 * Phone fp = (Phone)products[1];这时就会出现类型转换异常		 */		Computer fc = (Computer)products[1];		fc.coding();		System.out.println("--------------");		/*		 * 创建泛型工厂对象phoneFactory,泛型工厂的参数T位置传入Phone		 * 那么工厂phoneFactory变只能生产手机,不能再生产电脑		 * 如果想要生产电脑则需要,另外创建生产电脑的工厂		 */		GenericFactory<Phone> phoneFactory = new GenericFactory<Phone>();		//通过泛型工厂phoneFactory生产手机,参数只能传入Phone.class		Phone[] phone = phoneFactory.getProduct(Phone.class);		phone[0].call();		//创建电脑工厂,只能生产电脑		GenericFactory<Computer> computerFactory = new GenericFactory<Computer>();		Computer[] computer = computerFactory.getProduct(Computer.class);		computer[0].coding();	}}运行结果:拨打电话编写代码--------------拨打电话编写代码

小结:

使用父类型数组存储子类对象
优点:可以存储任何一种子类型
缺点:从数组中获取对象,并调用子类中特有方法时,需要强制类型转换
 存在类型转换异常的风险
使用泛型数组
优点:获取对象无限强制类型转换,也就不存在类型转换异常的风险
 类型的扩展性好
缺点:创建泛型相关的实例麻烦
使用时,泛型一旦传入参数,类型就固定了,导致类型单一(例如,上面的两个泛型工厂,可以看出两个不同的类型)


泛型相当于给类型加上一个参数,类似带参数的方法,方法的形参是声明某种数据类型的变量,使用小括号将参数括起来,传入的实参是某种数据类型的值或对象,在方法内便可以使用参数变量;而类型的形参只是一个占位符,使用尖括号括起来,传入的实参为类型(类名),在类的内部便可以将占位符作为数据类型使用;这就是泛型。
泛型应用最多的是集合。