泛型

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

泛型类

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 对象或它的子类型调用它。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值