黑马程序员——高新技术之泛型

来源:互联网 发布:python中re.search 编辑:程序博客网 时间:2024/06/06 12:45
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

泛型:JDK1.5后出现的新特性,解决了安全问题,是一个类型安全机制。
好处:1)将运行时期转移到编译时期,方便程序员解决问题。提高了安全性。2)避免了强制转换的麻烦ArrayList<String> 泛型语法Iterator<String>格式:通过<>来定义要操作的引用数据类型。
什么时候使用泛型?在集合中很常见,只要见到<>就要定义泛型。<>是用来接收数据类型的泛型技术:其实应用在编译时期,是给编译器使用的技术,到了运行时期,泛型就不存在了。为什么?因为泛型的擦除:也就是说,编辑器检查了泛型的类型正确后,在生成的类文件中是没有泛型的。
ArrayList<String> collection1 = new ArrayList<String>();ArrayList<Integer> collection2 = new ArrayList<Integer>();//打印结果:TRUESystem.out.println(collection1.getClass() == collection2.getClass());
在运行时,如何知道获取的元素类型而不用强转呢?泛型的补偿:因为存储的时候,类型已经确定了是同一个类型的元素,所以在运行时,只要获取到该元素的类型,在内部进行一次转换即可,所以使用者不用再做转换动作了。
泛型是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器编译带类型说明集合时会去除掉“类型”信息,使程序运行效率不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,例如,用反射得到集合,再调用其add方法即可。
public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, Exception {// TODO Auto-generated method stubArrayList<String> collection1 = new ArrayList<String>();ArrayList<Integer> collection2 = new ArrayList<Integer>();//打印结果:TRUESystem.out.println(collection1.getClass() == collection2.getClass());//通过反射调用collectin类中的add方法,加入元素abccollection2.getClass().getMethod("add", Object.class).invoke(collection2, "abc");System.out.println(collection2.get(0));}

一、泛型的通配符

定义一个方法,该方法用于打印出任意参数化类型的集合中的所有数据,该方法如何定义呢?
public static void printCollection(Collection<?> cols) {for(Object obj:cols) {System.out.println(obj);}//cols.add("string");//错误,因为它不知自己未来匹配就一定是Stringcols.size();//没错,此方法与类型参数没有关系cols = new HashSet<Date>();}
使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法。

限定通配符的上边界:
正确:Vector<? extends Number> x = newVector<Integer>();
错误:Vector<? extends Number> x = newVector<String>();
限定通配符的下边界:
正确:Vector<? super Integer> x = newVector<Number>();
错误:Vector<? super Integer> x = newVector<Byte>();
提示:限定通配符总是包括自己。
?只能用作引用,不能用它去给其他变量赋值

泛型集合类的综合案例:
HashMap<String, Integer>hMap = new HashMap<String, Integer>();hMap.put("zxx", 28);hMap.put("djv", 26);hMap.put("gvs", 23);hMap.put("fhg", 56);Set<Map.Entry<String,Integer>> mes= hMap.entrySet();for(Map.Entry<String,Integer> me : mes) {System.out.println(me.getKey() + ":" + me.getValue());}

二、自定义泛型

交换数组中的两个元素的位置的泛型方法语法定义如下:
private static <T> void swap(T[] a,int i,int j){T tmp = a[i];a[i] = a[j];a[j] = tmp;}
1)只有引用类型才能作为泛型方法的实际参数,swap(new int[3],3,5);语句会报告编译错误。
2)除了在应用泛型时可以使用extends限定符,在定义泛型时也可以使用extends限定符,例如,Class.getAnnotation()方法的定义。并且可以用&来指定多个边界,如<V extends Serializable & cloneable> void method(){};
3)普通方法、构造方法和静态方法中都可以使用泛型;
4)也可以用类型变量表示异常,称为参数化的异常,可以用于方法的throws列表中,但是不能用于catch子句中。
5)在泛型中可以同时有多个类型参数,在定义它们的尖括号中用逗号分。

练习一:编写一个泛型方法,自动将Object类型的对象转换成其他类型。
private static <T> T autoConvert(Object obj){return (T)obj;}
练习二:定义一个方法,可以将任意类型的数组中的所有元素填充为相应类型的某个对象。
private static <T> void fillArray(T[] a,T obj){for(int i=0;i<a.length;i++){a[i] = obj;}}
练习三、采用自定泛型方法的方式打印出任意参数化类型的集合中的所有内容。
/* * 在这种情况下,前面的通配符方案要比范型方法更有效, * 当一个类型变量用来表达两个参数之间或者参数和返回值之间的关系时, * 即同一个类型变量在方法签名的两处被使用, * 或者类型变量在方法体代码中也被使用而不是仅在签名的时候使用,才需要使用范型方法。 */public static <T> void printCollection2(Collection<T> collection){//collection.add(1);System.out.println(collection.size());for(Object obj : collection){System.out.println(obj);}}

编译器判断范型方法的实际类型参数的过程称为类型推断,类型推断是相对于知觉推断的,其实现方法是一种非常复杂的过程。
根据调用泛型方法时实际传递的参数类型或返回值的类型来推断,具体规则如下:
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) 
3)当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,且没有使用返回值,这时候取多个参数中的最大交集类型,例如,下面语句实际对应的类型就是Number了,编译没问题,只是运行时出问题:
fill(new Integer[3],3.5f)    static <T> void fill(T[] a, T v) 
4)当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型, 并且使用返回值,这时候优先考虑返回值的类型,例如,下面语句实际对应的类型就是Integer了,编译将报告错误,将变量x的类型改为float,对比eclipse报告的错误提示,接着再将变量x类型改为Number,则没有了错误:
int x =(3,3.5f)  ->  static <T> T add(T a, T b) 
5)参数类型的类型推断具有传递性,下面第一种情况推断实际参数类型为Object,编译没有问题,而第二种情况则根据参数化的Vector类实例将类型变量直接确定为String类型,编译将出现问题:
copy(new Integer[5],new String[5])  ->   static <T> void copy(T[] a,T[]  b);
copy(new Vector<String>(), new Integer[5])  ->   static <T> void copy(Collection<T> a , T[] b);

定义泛型类型
1)如果类的实例对象中的多处都要用到同一个泛型参数,即这些地方引用的泛型类型要保持同一个实际类型时,这时候就要采用泛型类型的方式进行定义,也就是类级别的泛型,语法格式如下:
public class GenericDao<T> {private T field1;public void save(T obj){}public T getById(int id){}}
2)类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的,例如,如下两种方式都可以:
GenericDao<String> dao = null;new genericDao<String>();
注意:
【1】在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。
【2】当一个变量被声明为泛型时,只能被实例变量、方法和内部类调用,而不能被静态变量和静态方法调用。因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有类级别的类型参数。


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 10岁儿童掉头发怎么办 1岁半宝宝掉头发怎么办 头发掉发严重怎么办吃什么 掉了一块钱很气怎么办 头发软又少怎么办盘头 头发出油掉的厉害怎么办 头发很油又少怎么办 18岁头发发量少怎么办 头发少长的慢怎么办 额头大发际线高怎么办 前额两边的头发变少了怎么办 吃减肥药掉头发怎么办 牙长智齿吃饭疼怎么办 头发稀少容易掉发怎么办 大把掉发头发稀少怎么办 头发掉厉害洗生发灵更掉怎么办? 额头两侧和头顶头发少怎么办 额头前面头发少怎么办天生的 头两边的碎头发怎么办 30岁后头发少怎么办 生完孩子头发少怎么办 1岁宝宝头发少怎么办 头顶上的头发少怎么办呢 22岁头顶头发稀少怎么办 头发又稀又少怎么办 头顶的头发越来越少怎么办 四岁宝宝头发稀少怎么办 一岁宝宝头发稀少怎么办 三岁宝宝头发稀少怎么办 头发油急着出门怎么办 长出的头发毛糙弯曲怎么办 头发薄还掉头发怎么办 头发少掉的厉害怎么办 一洗头就掉很多头发怎么办 生完孩子掉头发怎么办 甲减引起的肥胖怎么办 18岁掉头发严重怎么办 5岁儿童掉头发严重怎么办 18头发掉的严重怎么办 甲癌碘131后腮腺肿大怎么办 头发出油发丝细怎么办