黑马程序员-泛型
---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------
概念:
泛型(Generic)是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。Java语言引入泛型的好处是安全简单。使用java泛型,我们可以省掉强制类型转换。编译器会保留参数的类型信息,执行类型检查,执行类型转换操作。因此开发人员不需要自己确保类型转换的安全性,而把这个交给编译器去做。
泛型涉及的专业术语:
1、ArrayList<E> 整个称为泛型类型。
2、ArrayList<E>中的E称为类型变量或类型参数。
3、ArrayList<Integer>称为参数化的类型。
4、ArrayList<Integer>中Integer称为类型参数的实例或实际类型参数。
5、ArrayList<Integer>中<>念作typeof。
6、ArrayList称为原始类型。
使用泛型的规则和注意:
参数化类型和原始类型的兼容性:
List list=new ArrayList<String>();
List<String> list=new ArrayList();
这两种形式都是允许的。
类型擦除:
类型擦除指的是通过类型参数合并,将泛型类型实例关联到同一份字节码上。编译器只为泛型类型生成一份字节码,并将其实例关联到这份字节码上。类型擦除的关键在于从泛型类型中清除类型参数的相关信息,并且再必要的时候添加类型检查和类型转换的方法。
代码示例:
初识泛型:
public class GenericDeno {
/**
* 泛型的初步认识
* @param args
*/
public static void main(String[] args) {
//首先在jdk1.5之前没有泛型是这样做的
List list=new ArrayList();
list.add(1);
list.add(100l);
list.add("黑马程序员");
//在取出集合中的类型时由编码人员做强转,显然这样做是不安全的。虽然编译通过,但程序运行时会出错。
int i=(Integer)list.get(1);
//jdk1.5后用泛型的做法,可以看出泛型是在编译时期加了类型验证,让程序更安全。
List<String> list1=new ArrayList<String>();
list1.add("aaa");
list1.add("bbb");
list1.add("黑马程序员");
//这里不需要手动强制转换了,由编译器自动或隐式完成了
String value=list1.get(1);
}
}
理解擦除:
public class GenericDemo2 {
/**
* 理解擦除
* @param args
*/
public static void main(String[] args) {
//在运行时期,内存中的字节码中类型参数已被擦除,所以list_1和list_2关联的是同一份字节码。
//泛型只是在编译器加了类型验证。
ArrayList<String> list_1=new ArrayList<String>();
ArrayList<Integer> list_2=new ArrayList<Integer>();
Class clazz_1=list_1.getClass();
Class clazz_2=list_2.getClass();
System.out.println(clazz_1==clazz_2);//true
}
}
通配符(?):
public class GenericDemo3 {
/**
* ?通配符
* @param args
*/
//使用?通配符何以引用其他任意参数化的类型,?通配符定义的变量主要用作引用,可以调用与类型参数无关的方法,不可调用于类型参数有关的方法。
public static void main(String[] args) {
ArrayList<Integer> list=new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
//当不明确实际参数类型,可以用?号通配符。
printColl(list);
}
public static void printColl(Collection<?> coll){
//coll.add(e);不能调用与类型参数有关的方法。
//coll.size();可以调用于类型参数无关的方法。
for(Object object : coll){
System.out.println(object);
}
}
}
类型推断:
public class GenericDemo4 {
/**
* 类型推断
* @param args
*/
public static void main(String[] args) {
//传入的两个实际类型参数都是int,由于T只能是引用类型会先装箱成Integer类型,然后编译器会自动推断出两个实际类型参数的最小共有类型。
Integer integer=add(3,5);
//由于3.6和4都是number,所以推断出T的实际类型参数为Number类型。
Number number=add(3.5, 4);
//float f=add(3.5, 4);错误
//这里也和上面一样的推断,都是取最小共有类型。所以3和“abc”的共有类型是Object。
Object object=add(3, "abc");
swap(new String[]{"abc","xyz"}, 0, 1);
//swap(new int[]{1,2}, 0, 1);错误,因为泛型的类型参数只能是引用类型,不能使基本类型。
}
// 定义一个泛型方法
public static <T> T add(T x,T y){
return null;
}
//一个交换任意数组元素的方法
public static <T> void swap(T[] t,int i,int j){
T temp=t[i];
t[i]=t[j];
t[j]=temp;
}
}
异常中的泛型:
public class GenericDemo5 {
/**
* 泛型在异常中的应用
* @param args
* @return
*/
public static <T extends Exception> void Method() throws T{//声明泛型时必须说明T是Exception的子类。
try {
} catch (Exception e) {//catch中不能捕捉T
throw (T)e;//只能抛出T类型异常。
}
}
}
自定义泛型接口、泛型类和泛型方法:
public class GenericTest {
public static void main(String[] args) {
Box<String> name = new Box<String>("corn");
System.out.println("name:" + name.getData());
}
}
class Box<T> {
private T data;
public Box() {
}
public Box(T data) {
this.data = data;
}
public T getData() {
return data;
}
}
通过反射获取参数化类型和实际类型参数:
public class GenericDemo6 {
/**
* 反射获取参数化类型
* @param args
*/
public static void main(String[] args)throws Exception {
Method method=GenericDemo6.class.getMethod("method", Vector.class);//反射method方法
Type type=method.getGenericParameterTypes()[0];//通过method的方法获取泛型类型
ParameterizedType p=(ParameterizedType) type;
System.out.println(p.getActualTypeArguments()[0]);//class java.util.Date获取实际类型参数
}
public static void method(Vector<Date> v){
}
}
---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------