Java - 特性 - 泛型 - 整理

泛型是Java最具影响力的新特性之一,Java程序员需要深入理解这一特性。

   |--   从字面上看:泛型就是泛泛的指定对象所操作的类型,而不像常规方式一样使用某种固定的类型去指定。

   |--   从本质上看:泛型就是参数化类型,在创建类、接口、方法时可以用类型参数指定他们所要操作的数据类型。

   |--   类型参数最终都是要被实现的。


/**一个泛型类的简单实例**/

public class Gen<T> { //这里的T是类型参数的名称,又叫标记,是实际类型的占位符
	
	private T ob;	
	
	Gen(T ob){
		this.ob = ob;
	}	
	
	public T getOb(){
		return ob;
	}	
	
	public T setOb(T ob){
		this.ob = ob;
		return this.ob;
	}
	/*
	 * 因为Object是所有类的基类,所以这里可以使用getClass()和getName()。
	 * 如果使用到其他类的方法,是不行的。因为编译器并不知道T代表的具体类型。
	 * 如果要想使用其他类的方法,T要限定范围,也就是有界类型。
	 * 
	 * */
	public void showtype(){
		System.out.println("ob的类型是:"+ob.getClass().getName());
	}
}

/**下面是一个泛型类不合理的实现方式**/

public class GenDemo {

	public static void main(String[] args) {

		//Gen<Integer>将<Integer>传递给力Gen<T>的T。因为是在类上声明的所以对整个类都有效。
		Gen<Integer> Ob1 = new Gen(new Integer(123));
		Integer i = Ob1.getOb();
		System.out.println(i);
		Ob1.showtype();

		//Gen没有指定要传递的数据类型,编译后会被Object替换。new Gen<String>只是在创建Gen实例时指定Gen副本的T。
7		//这时构造函数相当于 Object ob = "123456"。Jdk7以后创建实例好像已经不用重新声明了、
		Gen ob2 = new Gen<String>("123456");
		Object s = ob2.getOb();
		System.out.println(s);
		ob2.showtype();//java.lang.String。getClass()获取的是创建"123456"这个对象的类。
		
		//ob1=ob2;/**同一种泛型可以对应多个版本(因为参数类型是不确定的),但是不同版本的泛型类实例是不兼容的。**/

	}

}


需要注意的是至始至终只有一个 Gen 类。Java编译器实际上只是擦除了所有泛型类信息,将之替换成必须的类型。


/**一个加深对泛型理解的例子**/

public class Tool{
	
	public <T> void add(Collection<? super T> a,T b){
//		<?>可以引用各种参数化的类型,可以调用与参数无关的方法,不能调用与参数有关的方法
//		a.addAll(a);// 编译失败。 boolean add(E e) 		 
		a.remove(b);// boolean remove(Object o) 			
	}
}

public class Test {

	public static void main(String[] args) {
		
		Tool tool =new Tool();
		//不完全确定list的类型。至少可以是Number,不管是什么类型,完全可以装下Integer
		List<? super Number> list = new ArrayList<Number>();
		//完全不确定list的类型。如果是Long就装不下,Integer
//		List<? extends Number> list = new ArrayList<Number>();//编译失败		
		list.add(new Integer(123));		
		tool.add(list,123);
	}
}



从上面可以看出泛型的一些使用规则:


      |--   泛型的类型参数只能是类类型(包括自定义类),不能是基础数据类型。

      |--   同一种泛型可以对应多个版本(因为参数类型是不确定的),但是不同版本的泛型类实例是不兼容的

      |--  泛型可以是静态的也可以是非静态的,静态内不能使用类型参数。静态方法的返回类型不能是类型参数

      |--   不能创建泛型异常类。

      |--   类型参数不能被实例化。


使用泛型的好处:


      |--   创建类型安全的代码,将运行时错误转换成编译时错误。
      |--   简化了处理过程,不再需要显示的使用 强制类型转换,所有类型转换都是自动、隐式进行的。
      |--   扩展了代码的重用能力。


泛型的语法:


      |--   声明泛型类:class class_name<type_param_list>

            |--   引用的语法:class<type_arg_list> var_name = newclass<type_arg_list>(cons_arg_list);


泛型的有界类型:

   |--   告诉编译器类型参数的范围。我们在实例化对象时所传递的类型也必须在这个范围之内。

   |--   限定上界 : <T extends Number>范围:Number及其子类。

   |--   限定下界 : <T super Number> 范围:Number及其父类。

   |--   限制方法和可以操作对象的类型。


泛型的通配符:

   |--   在泛型中可以使用通配符参数,用?来指定。表示未知类型

   |--   解决了指定占位符操作数据类型的局限性。可以同时操作多种数据类型。

   |--   <T extends Number> 和 <?extends Number> 的区别。从范围来看他们是相等的。但是。。。

   |--   通配符是不能被用做最终类型的,类型参数最终都是要被实现的。


/** 一个简单的有界类 **/

/*
 * 一个简单的计算数组和的类
 * 实现:
 * 要操作的数据类型很多,想到 泛型。
 * 所有数字类型都是Number的子类。
 * 在计算时要把对象转成基本数据类型进行运算。
 * 所以要限定标记的范围,告诉编译器我们要使用的数据类型都是Number的子类。
 * 
 * */

// 只有同一种数据类型才可以比较 T 
 public class Stats<T extends Number> {
	private T[] nums;

	Stats(T[] n) {
		nums = n;
	}

	T[] get() {
		return nums;
	}
	/*
	 *判断两个数组的大小
         *
         *这里的Stats<?>是函数的参数,是为了更安全的表述ob的类型。	
	 *
	 */
	public double compareSum(Stats<?> ob) {
		
		return add() - ob.add();
	}

	public double add() {

		double sum = 0.0;

		for (int i = 0; i < nums.length; i++) {
			sum += nums[i].doubleValue();
		}

		return sum;
	}

}

public static void main(String[] args) {
		
		Integer[] nums = {2,3,1,5,6,3,4};
//		Integer nums[] = {2,3,1,5,6,3,4,10};//数组的另一种表现形式。
		Number[] nums2 = {1.1,2.1,3.1,4};
		
		Stats<Integer> stats = new Stats<Integer>(nums);
		Stats<Number> stats2 = new Stats<Number>(nums2);
		double sum = stats.add();
		double sum2 = stats2.add();
		System.out.println(sum + "--" +sum2);	
		
		System.out.println(stats.compareSum(stats2));	
		
	}


泛型方法:

   |--   可以将类方法泛型化,即使他们的类不是泛型类。

   |--   泛型方法可以指定参数类型,返回值类型,参数之间的关系。

/*
 * 用泛型方法判断任意一个数是否在一个数组中  
 * 
 */
public class GenFunc {

	public static <T, Y extends T> boolean isIn(T t, Y[] y) {

		for (int i = 0; i < y.length; i++) {

			if (t.equals(y[i]))	return true;
		}

		return false;
	}

	public static void main(String[] args) {
		
		Integer[] nums = {21,32,545,87} ;
		int i = 21;
		if(isIn(i,nums)) System.out.println(i + "在nums中");		
		
		String[] strs = {"123","456","789"} ;
		String s = "123";
		if(isIn(s,strs)) System.out.println(s + "在nums中");			
		
	}

}

      |--   类型参数在方法返回前声明

   |--  泛型可以是静态的也可以是非静态的,静态方法不能使用定义在类上的类型参数。只能定义在方法上、

   |--   <T, Y extends T> 限定了 Y 的范围,保证了 T 和 Y 是同种数据类型。(强制类型安全


泛型接口:

   |--   泛型接口可以针对不同类型的数据进行实现。。。

   |--   一般而言如果实现了泛型接口,那么类也必须泛型化,至少应该实现接口的类型参数

   |--  子类都必须向上传递超类所需要的类型参数。

/*
 * GenInterface功能扩展。
 * 希望GenInterface可以实现计算出对象数组的最值功能。
 * 想到了接口
 * 对象比较想到Comparable
 * Comparable<T>的子类都具有比较功能。
 * 两个对象比较,必须数据类型一致,并且是Comparable<T>的子类才可以比较
 * 
 * */
/***
 * 比较大小要返回值,返回值类型不确定。 泛型。 
 * 只有同一种数据类型才可以比较 T 
 * 创建接口的时候最好限定类型范围,这符合逻辑思维
 ****/
interface minMax<T extends Comparable<T>> {
	T min(T[] vals);

	T max(T[] vals);
}

/***********
 * 
 * 子类必须现实接口的类型 minMax<T>,把T传递个接口
 * 不实现默认Object 泛型就没意义了 
 * 如果你觉得这里有点牵强,只要保证子类在范围内不就行了。那就错了。
 * 如果没有把类型传递给接口:那么就相当于
 * 接口的函数是:Object min(Object vals)
 * GenInterface:T min(T[] vals) 这是不行的。。。
 * 
 ******/
public class GenInterface<T extends Comparable<T>> implements minMax<T> {

	public T min(T[] vals) {
		
		T min = vals[0];
		for (int i = 1; i < vals.length; i++) 
			if (min.compareTo(vals[i]) > 0) min = vals[i];
		return min;
	}

	public T max(T[] vals) {
		
		T max = vals[0];
		for (int i = 1; i < vals.length; i++)
			if (max.compareTo(vals[i]) < 0)	max = vals[i];			
		return max;
	}

	public static void main(String[] args) {
		
		String[] vals = {"asdas","bsdas","esdasd","ffasd"};
		GenInterface<String> demo1 = new GenInterface<String>();
		String min = demo1.min(vals);
		String max = demo1.max(vals);
		System.out.println(min +"--"+ max);
	
	}

}

泛型类实例的强制类型转换:

   |--   只有当泛型类实例的类型相互兼容,类型参数也相同的时候才可以。

   |--   但是这样转换有意义?

   |--   GenInterface<String> demo1 = new GenInterface<String>();

    |--   GenInterface<Integen> demo2 = new GenInterface<Integen>();

   |--   虽然都GenInterface类型实例,但是已经是两种完全不兼容的对象了、这是泛型提高代码重用带来的、


泛型工作的原理:

   |--   为了兼容以前的老版本,java使用擦拭实现泛型。怎么擦拭

   |--   使用具体类型替换类型参数,如果没有显示的指定,就使用Object。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值