Java核心技术12:泛型程序设计

本文介绍了Java中的泛型程序设计,包括为何使用泛型、简单泛型类定义、泛型方法、类型变量限定,以及泛型在虚拟机中的表现和约束局限。泛型提供了更好的类型安全性和可读性,减少了强制类型转换的需要,尤其是在集合类中。同时,文章还讨论了泛型的继承规则、通配符类型和反射的应用。

泛型比使用Object变量,然后进行强制类型转换具有更好的安全性和可读性,对于集合类尤其有用。

12.1    为什么要使用泛型程序设计

        泛型意味着编写的代码可以被很多不同类型的对象所重用。

        ArrayList类有一个类型参数用来指示元素的类型:ArrayList<String> files;代码具有很好的可读性,一看就知道这个数组列表中包含的是String对象。编译器也可以很好地利用这个信息,当调用get的时候,不需要进行强制类型转换,编译器就知道返回值类型为String,而不是Object。

        通配符类型。

12.2    简单泛型类的定义

        一个泛型类ius具有一个或多个类型变量的类。Java中较常见是类型变量使用大写形式,使用E表示集合的元素类型,K和V分别表示表的关键字与值的类型,T表示任意类型。

        public class Pair<T>{

                private T first;    private T second;

                public Pair(){    first = null;    second = null;    }

                public Pair(T first, T second){    this.first = first;    this.second = second;    }

                public T getFirst(){    return first;    }

                public T getSecond(){    return second;    }

                public void setFirst(T first){    this.first = first;    }

                public void setSecond(T second){    this.second = second;    }

        }

        泛型类可以看作普通类的工厂,用具体的类型替换类型变量就可以实例化泛型类型,如Pair<String> ,Pair<String>(),String getFirst().

12.3    泛型方法

        class ArrayAlg{

            public    static    <T>    T    getMiddle( T[]    a ) {    return    a[a.length/2];    }

        }

        泛型方法可以定义在普通类中,也可以定义在泛型类中。类型变量放在修饰符的后面,返回类型的前面。

        调用泛型方法时,在方法名前的尖括号中放入具体的类型:String middle = ArrayAlg.<String> getMiddle(files);也可以直接调用    String middle= ArrayAlg.getMiddle(files);

12.4    类型变量的限定

        类或方法需要对类型变量加以限定,如将T限制为实现了Comparable接口的类:public static <T extends Comparable> T min(T[] a);,此时T所属的类必须具有compareTo方法。

        <T extends BoundingType>,表示T应该是绑定类型的子类型,T和绑定类可以是类或接口。

        <T extends Comparable & Serializable>,可以有多个限定,限定类型用&分隔。

12.5    泛型代码和虚拟机

        虚拟机没有泛型类型对象,所有对象都属于普通类。

        捂脸何时定义一个泛型类型,都自动提供了一个相应的原始类型,原始类型的名字就是删去类型参数后的泛型类型名。擦除类型变量,并替换为限定类型(无限定的变量用Object替换)。如果有多个限定的类型,就用第一个限定类型替换。

        public class Pair{

            public Pair(Object first, Object second){}

            public Object getFirst(){return first;}

            public void setFirst(Object first){ this.first = first; }

            private Object first;

        }

12.5.1    翻译泛型表达式

        当程序调用泛型方法时,编译器自动插入强制类型转换,即翻译为两条虚拟机指令:1.对原始方法的调用,2.将返回的Object类型强制转换。

12.5.2    翻译泛型方法

        编译器生成桥方法来保证多态。

12.5.3    调用遗留代码

12.6    约束与局限性

12.6.1    不能用基本类型实例化类型参数

        类型擦除后,Pair类含有Object类型的域,Object不能存储double、int等基本类型的值,只有Pair<Double>。

12.6.2    运行时类型查询只适用于原始类型

        虚拟机中的对象总有一个特定的类型(非泛型),所有的类型查询只产生原始类型:a instanceof Pair<String> 等价于a instanceof Pair。

        getClass方法总返回原始类型:Pair<String> str;  Pair<Employee> emp;  str.getClass()==emp.getClass()返回true,因为两次调用getClass都返回Pair.class。

12.6.3    不能抛出也不能捕获泛型类实例

        泛型类扩展Throwable不合法,不能在catch子句中使用类型变量,catch(T e)错误。

12.6.4    参数化类型的数组不合法

        如Pair<String>[] table = new Pair<String>[10];,将不能通过编译。

12.6.5    不能实例化类型变量

        不能使用new T(...),new T[...],或T.class。

        Class类本身是泛型,如String.class是一个Class<String>的实例,也是唯一实例。

        不能构造一个泛型数组,new T[2],类型擦除会永远构造Object[2]数组。

12.6.6    泛型类的静态上下文中类型变量无效

        不能在静态域或静态方法中引用类型变量。类型擦除后,将只剩下原始类型。

12.6.7    注意擦除后的冲突

12.7    泛型类型的继承规则

        Manager是Employee的一个子类,但Pair<Manager>不是Pair<Employee>的子类。

        无论S与T有什么联系,Pair<S>与Pair<T>没有什么联系。但是可以将参数化类型转换为一个原始类型,如Pair<Employee>是原始类型Pair的一个子类型。

        泛型类可以扩展或实现其他的泛型类。如ArrayList<T>类实现List<T>接口,一个ArrayList<Employee>可以转换为一个List<Employee>。

12.8    通配符类型

        Pair<? extends Employee>,表示任何泛型Pair的类型,它的类型参数是Employee的子类,如Pair<Manager>。

        类型Pair<Manager> 是Pair<? extends Employee>的子类型。

12.8.1    通配符的超类型限定

12.8.2    无限定通配符

12.8.3    通配符捕获

12.9    反射和泛型

12.9.1    使用Class<T>参数进行类型匹配

12.9.2    虚拟机中的泛型类型信息

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值