泛型

 * 泛型:

* jdk1.5的新特性,在没有泛型之前,程序可能存在安全隐患,如:在集合中取出元素时在强制类型转换过程中可能会发全ClassCastException,有了泛型之后就可以避免了这种情况的发生(不用强转),泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、 接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。在java中泛型是给提供给javac编译器使用的,可以限定集合的输入类型,在编译阶段挡住源程序的非法输入,编译器编译包含类型信息的类时给去掉"类型"信息,这一技术 叫做泛型的擦除,即生成的字节码是没有类型信息的,参数化类型和原始类型共享一份字节码。

* -->既然泛型是提供给javac编译器使用的,那么只要穿过编译器,就可以不受泛型 的约束可以添加任意类型的数据。


* --|关于泛型的一些常用术语:
*   Vector称为原始类型。
*   Vector<T>中的T称为类型变量或类型参数。
*   Vector<String>称为参数化类型。

*   Vector<String>中的String称为类型变量的实例或实际类型参数。


Vector<Integer> v1 =new Vector<Integer>();
		Vector v2 =new Vector();
        System.out.println(v1.getClass() == v2.getClass());//true
      
        // v1.add("abc");穿过编译器用反射来玩。
       Vector.class.getMethod("add", Object.class).invoke(v1, "abc");
     
     System.out.println(v1.get(0));

* 参数化类型与原始类型的兼容性。
* 1.参数化类型可以引用一个原始类型的对象:
*  Vector<String> v1=new Vector();要是不可以以前写的代码就废了。
* 2.原始类型可以引用一个参数化类型的对象:
*  Vector v2 =new Vector<String>();原来的方法接受一个集合参数,新的类型也要能够传进去。
*  
* 参数化类型不考虑类型参数的继承关系
* Vector<Object> v3=new Vector<String>();错,
* Vector<String> v4=new Vector<Object>();错,

* 编译器不允许创建泛型变量的数组,即在创建数组实体时,数组的元素不能使用参数化的类型。
* Vector<String>[] v5=new Vector<String>[3];


* 泛型中的通配符?
* 使用通配符?可以引用其它各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不可以调用与参数有关的方法.
*
* 泛型通配符的扩展:
* 上边界:<? extends T>:T的子类或T本身。
* 下边界:<? super T>:T的父类或T本身。

	private static void printCollection(Collection<?> collections)
	{
//		collections.add("abc");不能调用与类型参数有关的方法,因为它不知道自己未来匹配是什么类型。
		for(Object collection: collections){
			System.out.println(collection);
		}
	}
 定义泛型方法:

java语言的泛型类似于C++的模板,但限于表面,java的泛型几乎都是编译器中实现的,没有C++的强大。

	private static int add(int x,int y){
		return x+y;
	}
        //声明<T>这种类型,T只能是引用数据类型。
	private static <T> T add(T x,T y){
		return null;
	}

定义泛型类型:

如果类的实例对象的多处都要用到同一个泛型参数,即这些地方引用的泛型要保持同一个实际类型。可以定义在类上的泛型。

//定义格式
class DAO<E>{
public void add(E e )
{
}
public E findById(int id)
{
}
//编译不通过,静态不能使用泛型类型
public static E findById2(int id)
{
}
//要想定义泛型,必须自己玩
public static <E> E findById3(int id){}
}


泛型的类型推断:

编译器判断泛型方法的实际类型参数的过程 ,称为类型推断。这一过程非常复杂。

l根据调用泛型方法时实际传递的参数类型或返回值的类型来推断,具体规则如下:
1.当某个类型变量只在整个参数列表中的所有参数和返回值中的一处被应用了,那么根据调用方法时该处的实际应用类型来确定,这很容易凭着感觉推断出来,即直接根据调用方法时传递的参数类型或返回值来决定泛型参数的类型,例如:

   swap(new String[3],3,4)   à   static<E> void swap(E[] a, int i, int j)

2.当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型都对应同一种类型来确定,这很容易凭着感觉推断出来,例如:

   add(3,5)  à static <T> T add(T a, T b)

--当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,且没有使用返回值,这时候取多个参数中的最大交集类型,例如,下面语句实际对应的类型就是Number了,编译没问题,只是运行时出问题:

   fill(new Integer[3],3.5f)   à static <T> void fill(T[] a, T v)

3.当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,并且使用返回值,这时候优先考虑返回值的类型,例如,下面语句实际对应的类型就是Integer了,编译将报告错误,将变量x的类型改为float,对比eclipse报告的错误提示,接着再将变量x类型改为Number,则没有了错误:

   int x=(3,3.5f)   à static <T> T add(T a, T b)

4.参数类型的类型推断具有传递性,下面第一种情况推断实际参数类型为Object,编译没有问题,而第二种情况则根据参数化的Vector类实例将类型变量直接确定为String类型,编译将出现问题:

  copy(new Integer[5],new String[5]) à static<T> void copy(T[] a,T[]  b);

  copy(new Vector<String>(), newInteger[5]) àstatic <T> voidcopy(Collection<T> a , T[] b);






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值