A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© bjfanxc 中级黑马   /  2014-3-25 12:39  /  1503 人查看  /  0 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

一、了解泛型
1、ArrayList<E>类定义和ArrayList<Integer>类引用中涉及如下术语:
a)整个称为ArrayList<E>泛型类型
b)ArrayList<E>中的E成为类型变量或类型参数
c)整个ArrayList<Integer>成为参数化的类型
d)ArrayList<Integer>中的Integer称为类型参数的实例或者实际类型参数
e)ArrayList<Integer>中的<>念着typeof
f)ArrayList称为原始类型

2、参数化类型与原始类型的兼容性:
a)参数化类型可以引用一个原始类型的对象,编译报告警告,例如
Collection<String> c = new Vector();//可不可以不就是编译器一句话的事吗?
b)原始类型可以引用一个参数化类型的对象,编译报告警告,例如
Collection c = new Vector<String>();//原来的方法接受一个集合参数,新的类型也要能传进去

3、参数化类型不考虑类型参数的集成关系:
a)Vector<String> v = new Vector<Object>();//错误,不谢Object没错,写了就是明知故犯
b)Vector<Object> v = new Vector<String>();//也错误,

4、在创建数组实例时,数组的元素不能使用参数化的类型,例如,下面的语句有误:
Vector<Integer> vectorList[] = new Vector<Integer>[10];

5、思考题:下面的代码会报错吗?
Vector v1 = new Vector<String>();
Vector<Object> v = v1; //原始类型传给带参数类型,不会报错

二、泛型中的 ? 通配符
1、定义一个方法,该方法用于打印出任意参数化类型的集合中的所有数据,该方法如何定义呢?
错误方式:
public static void printCollection(Collection<Object> collection){
       
        for(Object obj : collection){
                System.out.println(obj);
        }
        collection.add("string");//没错
        collection = new HashSet<Data>();//会报错

}

正确方式:
public static void printCollection(Collection<?> collection){
       
        for(Object obj : collection){
                System.out.println(obj);
        }
        collection.add("string");//错误,因为不知道将要匹配的一定是String
        collection.size();//没错,此方法与类型参数无关
        collection = new HashSet<Data>();//会报错
        cols = new HashSet<Date>();

}

2、总结
使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,
不能调用与参数化有关的方法。

三、泛型中的?通配符的扩展
1、限定通配符的上边界:
正确:Vector<? extends Number> x = new Vector<Integer>();
错误:Vector<? extends Number> x = new Vector<String>();
2、限定通配符的下边界:
正确:Vector<? super Integer> x = new Vector<Number>();
错误:Vector<? super Integer> x = new Vector<Byte>();
3、提示:
限定通配符总是包括自己

四、泛型的综合应用

1、HashMap<String,Integer> maps = new HashMap<String,Integer>();
                maps.put("zxx", 25);
                maps.put("lxx", 35);
                maps.put("wxx", 45);
               
                Set<Map.Entry<String,Integer>> entrySet = maps.entrySet();
                for(Map.Entry<String, Integer> entry : entrySet){
                        System.out.println(entry.getKey()+":"+entry.getValue());
                }

2、数组的两个元素交换:
        private static <T>void swap(T[] a, int i, int j){
               
                T tmp = a[i];
                a[i] = a[j];
                a[j] = tmp;
        }

        在main方法中调用swap(new String[]{"abc","xyz","itheima"},1,2); //这是可以的

        在main方法中调用swap(new int[]{1,2,3,3,5},1,2);//这是不可以的,因为只有引用类型才能作为泛型方法的实际参数,应该把int改为Integer

3、可以用类型变量标示异常,称为参数化的异常,可以用于方法的throws列表中,但不能用于catch子句中
private static <T extends Exception> void sayHello() throws T{
        try{
       
        }catch(Exception e){
                throw (T)e;
        }
}

五、类型参数的类型推断

根据调用泛型方法时实际传递的参数类型或返回值的类型来推断,具体规则如下:
a)当某个类型变量只在整个参数列表中的所有参数和返回值中的一处被应用了,那么根据
调用方法时该处的实际应用类型来确定,这很容易凭着感觉推断出来,即直接根据调用方法时
传递的参数类型或返回值来决定泛型参数的类型,例如:
swap(new String[3],3,4);-->static <E> void swap(E[] a,int i,int j);

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

c)当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时
这多处的实际应用类型对应到了不同的类型,且没有使用返回值,这时候取多个参数中的最大
交集类型,例如,下面语句实际对应的类型就是Number了,编译没问题,知识运行时出问题:
fill(new Integer[3],3.3f) -->static <T> void fill(T[] a,T v)

d)当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时
这多处的实际应用类型对应到了不同的类型,并且使用返回值,这时候优先考虑返回值的类型,
例如,下面语句实际对应的类型就是Integr了,编译将报告错误,将变量X的类型改为float,
对比eclipse报告的错误提示,接着再讲变量x类型改为Number,则没有了错误:
int x = (4,4.4f) --> static <T> T add(T a,T b)

f)参数类型的类型推断具有传递性,下面第一种情况推断实际参数类型为Object,编译则没有
问题,而第二种情况则根据参数化的Vector类实例将类型变量直接确定为string类型,编译将
出现问题:
copy(new Integer[5],new String[5])-->static <T> void copy(T[] a,T[] b)

六、定义泛型类
1、如果累的实例对象中的多处都要用到同一个泛型参数,即这些地方引用的泛型类型要保持同一个
实际类型时,这时候就要采用泛型类型的方式进行定义,也就是类级别的泛型,语法格式如下:
public class GenericDao<T> {
        private T field;
        public void save(T obj){}
        pubilc T getById(int id        ){}
}

2、类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的,例如,如下两种方式都可以
GenericDao<String> dao = null;
new GenericDao<String>();

3、注意:
a)在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能使基本类型。
b)当一个变量被声明为泛型时,只能被实例变量和方法调用(还有内嵌类型),而不能被静态变量和静态方法
调用。因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有类级别的类型参数。

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马