Java泛型

1、为什么要使用泛型?

        泛型解决了手动强制类型转换的问题,使用泛型的程序在编译阶段就能判断出存在的强制转换问题,而不是到运行时才报错,使得程序更为安全。

        提高了编程的灵活性。

2、泛型类/接口

        定义泛型类,指定泛型不一定使用T,也可以使用R、E、T1、T2任意名字:

public class Pair<T>{

    private T first;
    private T second;

    public T getFirst(){ return first; }
    public void setFirst(T newValue){ first = newValue; }

}

        泛型类的继承有两种情况:继承泛型、继承具体类型

/*
    第一种
*/
class BluePair<T> extends Pair<T>{
    
}

/*
    第二种
*/
class RedPair<String> extends Pair<String>{

}

        带有限定的泛型:

public class Pair<T extends Serializable> {
}

         限定可以包含一个类和若干个接口,类必须放在第一个:

public class Pair<T extends Number & Serializable & Comparable> {
}

3、泛型方法

        泛型类中的方法 不是 泛型方法。泛型方法是单独声明的,可以在任何类中声明泛型方法:

public class Pair<T> {
    private T first;
    
    public <R> T function(R param){ return first; }
    public <R, E> void function2(R param1, E param2){}
}

        其中R、E为泛型方法中的参数类型,普通类中也可以声明泛型方法。

4、通配符

        通配符有3种写法:

<?>
<? extends Human>
<? super Student>

        使用较多的是后2种带有限定的通配符写法。

        PECS(Producer Extends Consumer Super)口诀:<? extends XClass>的类只能从中数据而不能增删或修改、<? super XClass>的类只能向内部输入数据而不能读取。

        分析原因。假设有如下场景:

Pair<Student> xiaoming = new Pair<>();
Pair<? extends Human> human = xiaoming; // OK
human.setFirst(xiaoming); // 编译错误

          假设,Pair<? extends Human>的方法如下:

<? extends Human> getFirst();
void setFirst(? extends Human human);

        其中setFirst方法的参数是模糊的,无法确定传入的参数human到底是什么类型,有人可能要说“那我把human当Human类来用不就行了?”,是的,那为什么不把方法命名为setFirst(Human human)呢?所有能合法使用<? extends Human>作为参数类型的方法都能用Human进行代替,因此上限通配符并不适合作为方法的参数类型来使用,只能用作引用类型。

        对于getFirst,得到的对象必须使用Human或其父类作为其引用类型。

        再看看<? super Student>的使用场景:

Pair<? super Student> superStu;

superStu.setFirst(new Pair<CollegeStudent>()); // OK
superStu.setFirst(new Pair<Human>()); // Error

Pair<Human> superStu.get(1); // Error
Object superStu.get(1); // OK

        对于其中的set方法,无法判断其传入的参数类型,只能传入Student及其子类对象,因为这样在使用这些对象时可以统一用Student进行引用。

        对于get方法,考虑如下场景:

public void doSome(Pair<? super Student> someone){
    someone.methodOfHuman();
}

public static void main(){
    Pair<Object> o = new Pair<>();
    doSome(o); // Error
}

        无法判断方法参数中的someone应该使用何种引用类型(除了Object),所以下限通配符不适合使用get方法,只适合向其中写入下限类及其子类。

5、类型擦除

        编译后的程序将不再带有泛型,泛型只在编译检查阶段使用,保证运行时能够安全运行,因此没必要将泛型保留到运行阶段,所有的泛型都将使用泛型上限类型取代,例如,<T>泛型类和泛型方法中,所有的T引用将使用Object进行取代、<T extends Human>则使用Human取代。

        受此影响,不能对一个泛型类数组进行初始化,考虑如下场景:

// 有如下Pair:
class Pair<T>{

    public methodOfPair(T param){ ... }

}

Pair<String>[] table = new Pair<>[10]; // 假设能通过编译,运行时由于类型擦除,table实际为Pair数组
Object[] objArray = table; // 这是合法的
objArray[0] = 1234; // Error,因为table有数组元素类型检查机制,只允许元素类型为Pair
objArray[0] = new Pair<Human>(); // 类型擦除后,实际为objArray[0] = new Pair();这是能顺利运行的
Pair<String> first = table[0]; // OK,但first实际为Pair<Human>
first.methodOfPair("param is String"); // Error,这是因为methodOfPair需要传入的是<T>类型的参数,而运行时first实际调用的是methodOfPair(Human param),但这里传入的是一个String参数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值