<a href="http://www.itheima.com"
target="blank">android培训</a>、<a
href="http://www.itheima.com" target="blank">java培训
</a>、期待与您交流!
泛型
泛型:是提供给java编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入.编译器编译带类型说明的集合时,会去除掉”类型”信息,使用程序运行效率不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一致.由于编译生成的字节码会去掉”类型信息”,只要能跳过编译器,就可以往某个泛型集合中加入其它的类型的数据,例如用反射得到集合,再调用其add方法即可.
为什么要使用泛型:
泛型是JDK1.5所有新特性中最难深入掌握的部分,因此只需要掌握基本的内容就差不多了.没有使用泛型时,只要是对象,不管是什么类型的对象,都可以存储进同一个集合中.使用泛型集合,可以将集合中的元素限定为一个特定类型,集合中只能存储同一个类型的对象,这样更安全;并且当从集合中获取一个对象时,不需要对对象进行强制类型转换,这样就更加方便.对于支持泛型的对象,如果不采用泛型,则会报警告.
有了泛型,就可以创建独立于被包含类型的类和方法,我们不必给不同的类型编写功能相同的许多方法和类,只创建一个方法和类即可.
另一个是减少代码的选项是使用Object类,但Object类不是类型安全的.泛型类使用泛型类型,并可以根据需要用特定的类型替换泛型类型.这种保证了类型安全性,如果某个类不支持泛型类,编译就会出现错误.
泛型的书写形式:
对持泛型的对象<类型变量>
如:
ArrayList<E>类定义和ArrayList<Integer>类引用中涉及如下术语
Ø 整个称为ArrayList<E>泛型类型
Ø ArrayList<E>中的E称为类型变量或者类型参数
Ø 整个ArrayList<Integer>称为参数化的类型
Ø ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数
Ø ArrayList<Integer>念法:ArrayList typeof Integer
Ø ArrayList称为原始类型
参数化类型与原始类型的兼容性:
Ø 参数化类型可以引用一个原始化类型的对象,编译报告:警告
Collection<String> c = new Vector();
Ø 原始类型可以引用一个参数化类型的对象,编译报告:警告
Collection c = new Vector<String>();
参数化类型不考虑类型参数的继承关系
Ø Vector<String> v = new Vector<Object>();//错
Ø Vector<Object> v2 = new Vector<String>();//错
下面的代码不会报错:
Vector v = new Vector<String>();
Vector<Object> v2 = v;//把一个原始类型给了一个参数化类型
JDK1.5以前的集合类中存在的问题
ArrayList collection = new ArrayList();
collection.add(1);
collection.add(1L);
collection.add(“abc”);
int I = (Integer)collection.get(1);//编译要强制类型转换且运行时出错!
JDK1.5的集合希望你在定义集合时,明确表示你要向集合中添加哪种类型的数据,定义了泛型,就无法加入指定类型以外的数据.
ArrayList<Integer> collection2 = newArrayList< Integer >();
Collection2.add(1);
Collection2.add(1L); //编译时错误
Collection2.add(“abc”); //编译时错误
int i = collection2.get(0); //不需要再进行类型转换
同一个对象,使用了泛型和没有使用泛型的字节码文件对象是同一个字节码文件对象
如:
ArrayListcollection = new ArrayList();
ArrayList<Integer> collection2 = new ArrayList<Integer>();
if(collection.getClass()== collection2.getClass())//是否为同一份字节码文件对象
{
System.out.println("字节码是同一份!");
}
该代码运行的结果是肯定的:他们是属于同一份字节码文件.
/*
* 通过反射跳过泛型,向集合中加入数据.从而也说明同一个对象使用了泛型和没有使用泛型是使用的同一个getClass
*/
importjava.util.ArrayList;
public class Demo2
{
public static void main(String[] args)throws Exception
{
ArrayList<Integer> arr = new ArrayList<Integer>();
arr.getClass().getMethod("add",Object.class).invoke(arr,"aa");
//打印是否加入进字符串"aa",最后打印结果为:aa
System.out.println(arr.get(0));
}
}
泛型中的 ? (通配符)
问题:定义一个方法,该方法用于打印出任意参数化类型的集合中的所有数据,如何定义?
错误方式:
public void printCollection(Collection<Object> cols)
{
for (Object obj : cols) //Object代表类型变量
{
System.out.println(obj);
}
}
正确方式:
public void printCollection(Collection<?> cols)
{
for (Object val : cols) //Object代表类型变量,直接打印也行,val也是Object类型
{
System.out.println(val);
}
}
总结:使用?通配符可以引用其它各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法.
泛型中的? 通配符的扩展
限定通配符的界:
正确:
Ø Vector<?extends Number> v = new Vector<Integer>();//上边界?继承至Number
Ø Vector<?super Integer> v = new Vector<Number>();//下边界?必须是Integer的父类
错误:
Ø Vector<?extends Number> v2 = new Vector<String>();
Ø Vector<?super Integer> v3 = new Vector<Byte>();//同级
提示:限定通配符总是包括自己.
泛型演示:
importjava.util.*;
public class Demo3
{
public static void main(String[] args)
{
//实例化一个HashMap集合对象
HashMap<String,Integer> maps = new HashMap<String,Integer>();
//向集合中添加数据
maps.put("zhangsan", 22);
maps.put("wangwu", 34);
maps.put("chengjuan", 32);
//声明entry类型的Set集合,用于输出HashMap里面的数据
Set<Map.Entry<String,Integer>> entrySet = maps.entrySet();
//输出集合
for(Map.Entry<String,Integer> entry :entrySet)
{
System.out.println("姓名:" + entry.getKey() +"...年龄:" + entry.getValue());
}
}
}
定义泛型方法
交换数组中两个元素位置的泛型如下:
//交换数组中的两个元素的位置
public static <E> void swap(E[] arr,int i,int j)//i j表示元素的下标
{
E temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
注意:要调用上面的代码泛型参数必须为引用类型.普通方法,构造方法,静态方法中都可以使用泛型.编译器不允许创建类型变量的数组.
Java中的泛型类型或者泛型类似于C++中的模板.但是这种相似性仅限于表面,java语言中的泛型基本上全部是在编译器中实现.用于编译器执行类型检查和类型推断,然后生成普通的非泛型的字节码,这种实现技术称为擦除(编译器使用泛型类型信息保存类型安全,然后在生成字码之前将其清除).这是因为扩展虚拟机指令集来支持泛型,被认为是无法接受的.这会为java厂商升级其JVM造成难以逾越的障碍.所以java的泛型采用了完全在编译器中实现的擦除方式.