泛型

来源:互联网 发布:python 函数定义输出 编辑:程序博客网 时间:2024/06/10 00:17

泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。使用泛型,意味着编写的代码可以被很多不同类型 的对象所重用。

泛型类

public class ClassTest<T> {    public T firtsFiled;    public T secondFiled;    public void setFirtsFiled(T data){        firtsFiled = data;    }    public void setSecondFiled(T data){        secondFiled = data;    }    public T getFirtsFiled(){        return firtsFiled;    }    public T getSecondFiled(){        return secondFiled;    }    public static <T> T getStaticData(T data){        return data;    }}

以上是一个简单的泛型类,T成为类型变量,一般使用大写字母命名。在Java中常用变量E表示集合的元素类型,K和V表示关键字与值的类型,T表示任意类型(约定俗成的用法,事实随便一个字母都行)。
当实例化泛型类型需要用具体类型替代类型变量
例如:

ClassTest<String> one  = new ClassTest<>();ClassTest<Integer> two  = new ClassTest<>();one.setFirtsFiled("data1");      two.setFirtsFiled(123);

泛型方法
泛型方法可以定义在普通类或泛型类中,与普通方法不同,泛型方法可以在调用它的时候定义类型变量。
例如 public static <T> T getStaticData(T data) 就是一个泛型方法,在方法的返回值前加上 <T> ,在调用时指定类型变量,如下:

Integer staticData = ClassTest.getStaticData(9090);String hello = ClassTest.getStaticData("hello");

类型变量的限制
先看这个代码

public static <T extends Comparable> boolean getMinData(T data){    return  data.compareTo(data) > 0 ;}

之所以在定义泛型方法时给 <T> 继承 Comparable 接口,是因为 data 的类型无法确定,不能保证对象都有 compareTo 方法。
一个类型变量或通配符可以有多个限定,例如 T extends Comparable & Serializable
限定类型用“&”分隔,而逗号用来分隔类型变量。
在Java的继承中,可以根据需要拥有多个接口超类型,但限定中至多有一个类。如果用一个类作为限定,它必须是限定列表中的第一个。(core Java)

类型擦除
在虚拟机中没有泛型类型对象,所有对象都属于普通类。Java中的泛型基本上都是在编译器这个级别实现的,生成的字节码信息中是不包含泛型中的类型信息的。在定义一个泛型类型时, 都会提供一个删去类型参数后的原始类型,擦除类型变量,并替换为限定类型(无限定的变量用Object)。
例如以上的泛型类擦除类型后的原始类型:

public class ClassTest {    public Object firtsFiled;    public Object secondFiled;    public void setFirtsFiled(Object data){        firtsFiled = data;    }    public void setSecondFiled(Object data){        secondFiled = data;    }    public Object getFirtsFiled(){        return firtsFiled;    }    public Object getSecondFiled(){        return secondFiled;    }    public static Object getStaticData(Object data){        return data;    }}

所以,不能存在如此两个方法,编译器会提示错误

public String getFirtsFiled(T a){   return "1";}public String getFirtsFiled(Object w){   return "1";}

通配符类型
在泛型操作中进行参数传递时泛型类型必须匹配才能传递,使用通配符来设置传递参数的类型
例子,其中Man是Peple的子类,不必关心实现:

public class SubClass {    public void test(ClassTest<Peple> p){    }    public void transfer(ClassTest<? extends Peple> p ){    }    public static void main(String[] args) {        SubClass sub = new SubClass();        ClassTest<Man> tt = new ClassTest<>();        //sub.test(tt);错误        sub.transfer(tt);        ClassTest<? extends Peple> tt2 = new ClassTest();        //tt2.setFirtsFiled(new Man());错误        //tt2.setFirtsFiled(new Peple());错误        Peple pp =tt2.getSecondFiled();    }}

当调用 sub.test(tt); 时发生错误,我们不能把一个 ClassTest<Man> 传递给这个方法, tt 的类型是 ClassTest<People> ,但定义 public void transfer(ClassTest<? extends Peple> p ) 使用通配符后 sub.transfer(tt); 可以正确使用。
再看下面的两个错误,使用通配符后set方法和get方法显然为

void setFirtsFiled(? extends Peple)? extends Peple getSecondFiled()

编译器只知道要将 People 的子类型,但未具体指定,所有set方法会报错,而get方法就没这个问题,有点类似于多态的子类对象指定父类引用,返回一个 People 子类型没有问题。

通配符的超类限定
extends 来匹配子类,当然也有 super 来指定超类型限定,使用的意思刚好相反

public class SubClass {    public void test(ClassTest<Man> m){    }    public void transfer(ClassTest<? super Man> m ){    }    public static void main(String[] args) {        SubClass sub = new SubClass();        ClassTest<Peple> tt = new ClassTest<>();        //sub.test(tt);错误        sub.transfer(tt);        ClassTest<? super Man> tt2 = new ClassTest();        tt2.setFirtsFiled(new Tom());//Tom继承自Man        tt2.setFirtsFiled(new Man());        //tt2.setFirtsFiled(new Peple());错误        //Peple pp = tt2.getSecondFiled();错误    }}

transfer 方法允许使用通配符方式传进一个 ClassTest<Peple> ,因为 PeopleMan 的超类。下面的两个错误是因为此时不确定get方法返回的对象类型无法保证,只能把它赋给一个 Object ,而set方法可以使用任意 Man 对象或它的子类型调用它。

原创粉丝点击