泛型

来源:互联网 发布:python 伪装成浏览器 编辑:程序博客网 时间:2024/06/06 03:30

1.为什么需要泛型?

我们看下面一个例子:
public class GenericTest {    public static void main(String[] args) {        List arrayList = new ArrayList();        arrayList.add("zhang");        arrayList.add("genericTest");        arrayList.add(2017);                for (int i=0; i<arrayList.size(); i++) {            //1.java.lang.ClassCastException            //String message = (String) arrayList.get(i);            //System.out.println("message:"+message);            //2.OK            Object message = arrayList.get(i);            System.out.println(message.getClass()+" "+"message:"+message);        }    }}

       定义一个ArrayList类型的集合,先向其中加入两个字符串类型的值,随后加入一个Integer类型的集合,此处完全没问题,因为此时的arrayList默认的类型为Object类型。在之后的for循环中由于忘记了arrayList类中含有Integer类型的元素,将Integer类型强制转换为String类型,出现1处的错误。

       在上面的编码过程中,主要存在两个问题:

       (1)将一个对象放入到一个集合中,集合不会记住此对象的类型,当再次从集合中取出此对象时,由于该对象在编译类型为Object类型,但其运行时类型依然为其本身类型,所以在1处运行时出现 Java.lang.ClassCastException 异常,导致此类错误在编译过程中不易被发现。

       (2)取出集合元素时,需要人为强制转化为具体的目标类型

2.什么是泛型?

        泛型,即“参数化类型”,顾名思义,就是将原来的具体的类型参数化,就像方法中的变量参数,此时类型也定义成参数形式(“类型形参”),然后在使用时传入具体的类型(”类型实参”)。

        将上面的例子采用泛型的写法。

public class GenericTest {    public static void main(String[] args) {        List<String> arrayList = new ArrayList<String>();        arrayList.add("zhang");        arrayList.add("genericTest");        //arrayList.add(2017);1.提示编译错误                for (int i=0; i<arrayList.size(); i++) {            String message = arrayList.get(i);//2        System.out.println("message:"+message);        }    }}
     采用泛型写法后,在1处加入一个Integer类型的对象时会出现编译错误,通过List<String>限定arrayList集合中只能含有String类型的元素,因此在2处不需要强制类型转换。

3.泛型接口、泛型类和泛型方法

(1)泛型类定义格式:

访问权限 class 类名称<泛型类型标识1,泛型类型标识2,..泛型类型标识n>{    访问权限 泛型类型标识 变量名称;    访问权限 泛型类型标识 方法名称(){};        访问权限 返回值类型声明 方法名称(泛型类型标识 变量1,泛型类型标识 变量2,...){}   

(2)泛型对象的定义格式:

类名称<具体类> 对象名称 = new 类名称<具体类>();      
简单泛型类和方法使用:
package generic;class Point<T> {private T var;public void setVar(T var) {this.var = var;}public T getVar() {return this.var;}}public class GenericClass {public static void main(String[] args) {Point<String> name = new Point<String>();name.setVar("zhang");System.out.println(name.getVar());}}

在泛型接口、泛型类和泛型方法的定义过程中,常见 T、E、K、V 形式的参数表示泛型形参,思考:对于不同传入的类型实参,生成的相应对象实例类型是不是一样呢?

package generic;public class GenericType {public static void main(String[] args) {Point<String> stringPoint = new Point<String>();    Point<Integer> integerPoint = new Point<Integer>();    System.out.println(stringPoint.getClass());//class generic.Point    System.out.println(integerPoint.getClass());//class generic.Point    System.out.println(stringPoint.getClass() == integerPoint.getClass());//true}}

由此发现,在使用泛型时,虽然传入了不同的泛型实参,但并没有真正意义上生成不同的类型,泛型类在内存中只有一个,即还是原来最基本的类型(本例中为Point)。泛型只作用于代码编译阶段,成功编译过后的class文件中是不包含任何泛型信息的,泛型信息不会进入到运行时阶段,泛型类型在逻辑上可以看作是多个不同的类型,实际上都是相同的基本类型。

4.类型通配符

接着上面内容,思考:Point<Number>和Point<Integer>实际上都是Point类型,那么在逻辑上,是否可以将二者看成具有父子关系的泛型类型呢?简单例子:

package generic;public class GenericGeneral {public static void main(String[] args) {Point<Number> name = new Point<Number>();Point<Integer> age = new Point<Integer>();getVar(name);//The method getVar(Point<Number>) in the type GenericGeneral is not applicable //for the arguments (Point<Integer>)//getVar(age);1}public static void getVar(Point<Number> data) {System.out.println("data:"+data.getVar());}}
编译上面代码,在1处提示错误信息,因此在逻辑上Point<Number>不能视为Point<Integer>的父类。因此引入类型通配符的概念,逻辑上用来表示这种继承关系。看下面类型通配符使用实例:

package generic;public class Generic_01 {public static void main(String[] args) {Point<String> name = new Point<String>();Point<Integer> age = new Point<Integer>();Point<Number> number = new Point<Number>();getVar(name);getVar(age);getVar(number);}public static void getVar(Point<?> data) {System.out.println(data.getVar());}}
类型通配符上限和类型通配符下限,比如在上面的例子上,定义一个同样的相似的getVar方法,但是实参类型进一步限制,只能是Number类型及其子类,具体实例如下:

package generic;public class Generic_01 {public static void main(String[] args) {Point<String> name = new Point<String>();Point<Integer> age = new Point<Integer>();Point<Number> number = new Point<Number>();getVar(name);getVar(age);getVar(number);//getUpperNumberVar(name);1getUpperNumberVar(number);2getUpperNumberVar(age);3}public static void getVar(Point<?> data) {System.out.println(data.getVar());}public static void getUpperNumberVar(Point<? extends Number> data) {System.out.println(data.getVar());}}
在1处代码将会出现错误提示,2和3处调用正常。

类型通配符下限形式为Point<? super Numer>,其含义与类型通配符上限正好相反。


原创粉丝点击