Java泛型--学习笔记

1  泛型参数的一些约定

类型参数使用大写字母,且较短。在Java库中,使用变量E表示集合的元素类型,K和V表示关键字和值的类型。T(需要时还可以用临近的U和S)表示“任意类型”。

2  泛型定义

2.1  简单泛型类

泛型类可看作普通类的工厂。

只关注泛型,而不关注数据存储的细节。

public class Pair<T>{

    private T first;

    private T second;

}

2.2  泛型方法

类型参数放在修饰符后,返回类型前。

1、定义

public class Plant{

    public static <T>  T getProduct(T.. a){

     ...

    }

}

2、调用

具体参数放在方法名前。具体类型一般省略,由编译器推断出具体类型。

Plant.<Material>getProduct(materialA, materialB);

2.3  类型参数限定

使用关键字extends限定。

1、一个限定

public static <T extends Cloneable> List<T> cloneObjs(T...objs){

   ...

}

2、多个限定

使用&指定多个限定。

注意:类类型要放在最前边,且只能有一个。

T extends Comparable & Cloneable

3  类型擦除

虚拟机中没有泛型类型,任何一个泛型类都会提供相应的原始类型。

原始类型名为泛型类删除类型参数后的类型名;类型参数用其限定类型替换。若无限定,则用Object替换;若有多个限定,则用第一个限定类型替换。

3.1  泛型类的原始类型

public class Pair<T>{

    private T first;

    private T second;

}

上述泛型类,类型擦除后的原始类型为:

public class Pair{

    private Object first;

    private Object second;

}

3.2  泛型方法类型擦除

public static <T>  T getProduct(T.. a){

     ...

}

类型擦除后为:

public static  Object getProduct(Object.. a){

     ...

}

3.3  桥方法  

类型参数擦除后,有可能在类中会产生多个签名相同的方法(如,继承自父类的方法及在本类中定义的方法,由于类型参数被替换为Objcct后,方法签名变为相同)。

出现此种情况,会生成桥方法,以保持多态。

桥方法不仅用于泛型类型,当一个方法覆盖另一个方法时(注:非override),可指定更具体的返回类型(协变)。此处就用到列桥方法。

4  约束

1、类型参数不能为基本类型(由于类型擦除)

2、泛型类型不能进行类型转换或使用instanceof(因为运行时没有泛型类型,只有原始类型)

3、对于泛型,getClass只返回原始类型,如:

Pair<String> stringP=..;

Pair<Double> doubleP=..;

stringP.getClass()==doubleP.getClass();

它们都返回Pair.class.

4、不要创建泛型数组(由于类型擦除)

如:Pair<String>[] pairs=new Pair<String>[12];

类型擦除后,实际创建的是new Pair[];这导致以下赋值能通过编译检查,但运行时会导致类型错误。

pairs[0] = new Pair<Double>();

可替代方法

(1)声明通配类型的数组,再进行类型转换:

Pair<String> pairs=(Pair<String>[ ])new Pair<?>[8];   (非类型安全)

(2)类型安全

ArrayList:ArrayList<Pair<String>>

5、不能实例化类型参数变量(如:T t =new T()或T[] ts=new T[3],因为类型擦除后,变为new Object())

解决方法:

(1)对于普通类

可通过反射获得:Class类本身是泛型,可以使用Class.newInstance()方法构造泛型对象,如下:

public static <T> generate(Class<T> class){

    class.newInstance()

}

调用方式如下:generate(Double.class);   //注:Double.class是Class<Double>的实例,且是单例。

注意对于泛型,getClass只返回原始类型,如:

Pair<String> stringP=..;

stringP.getClass()返回类型是Pair.class

(2)对于数组

利用反射,调用Array.newInstance,如下:

public static<T extends Comparable> T[] getArry(T...a){

  T[ ] array=(T[ ])  Array.newInstance(a.getClass().getComponentType(), 3);

}

5  泛型类型的继承规则

5.1  普通类与其泛型化后的类的区别

Cat是Animal的子类,而ArrayList<Cat>不是ArrayList<Animal>的子类

注意:无论T,U有什么关系,ArrayList<T>和ArrayList<U>没任何关系。假设,它们有继承关系,则如下逻辑显然是错误的:

ArrayList<Cat> cats=new ArrayList<Cat>();

ArrayList<Animal> animals=cats;

animals.add(new Dog());   //该句语法正确,但如果允许上一语句的转换,则就把Dog加入列Cat的列表,这显然不正确

注意:数组与泛型不同,如下数组转换是正确的:

Cat[] cats=new Cat[3];

Animal[] animals=cats;  //转换正确

animals[0] = new Dog();   //此句会抛出异常,虚拟机会对数组赋值进行检查

5.2 泛型化的类型与原始类型关系

可以将泛型化的类型赋给原始类型。

5.3  泛型类的继承

泛型类可以继承其它泛型类或泛型接口。

6  通配符类型

固定的泛型类型不是很灵活,可以使用通配符类型。

通配符限定与类型参数限定类似,且可以提供超类型限定

例如:

List<? extends Animal>表示任何泛型List类型,其类型参数是Animal的任何子类,如List<Cat>或List<Dog>,但不能是List<Double>.

注意:List<T extends Animal>代表一组类型,而List<? extends Animal>代表单一的类型,只是无法确定是哪种类型。

 List<? super Cat> 表示任何泛型List类型,且其类型参数是Cat的任何父类。

分别

6.1  通配符的子类型限定(List<? extends Animal>)

继承关系

List<Cat>和List<Dog>是List<? extends Animal>的子类。

使用子类型通配符限定,getter方法是安全的,而setter方法是不安全的,如以下赋值语句不正确:

List<Cat> cats=new List<Cat>();

List<? extends Animal> animals= cats;  //正确,List<Cat>是List<? extends Animal>子类,可以类型转换

animals.add(new Dog());     //错误不可用setter方法。add(? extends Animal),只知道接受Animal的子类型实例,但不知具体是什么类型。

而如下setter语句是正确的:

Animal animal=animals.get(0);    //正确可用getter方法. 返回?extends Animal类型,其是Animal的子类。

6.2  通配符的超类型限定

? super Cat

超类型限定与子类型限定正好相反,其可为方法提供参数,但不能使用方法的返回值。

List<? super Felidae> felidaes= ...

felidaes.add(new Cat());     // 正确,可用setter方法  可用任意Felidae对象,或其子类型

Animal animal = felidaes.get(0)   //错误,不可用getter方法  返回 ? super Felidae,但不知具体的父类型

6.3  无限定通配符(?)

无限定通配符不是类型参数,不能在代码中使用?作为一种类型。

无限定通配符指代明确的类型(其不是泛型),只是无法确定具体是哪种类型的通配符。

Brand<?>

getter方法:? get(i)   //返回值只能赋给Object类型

setter方法:add(?)   //不能被调用,参数为Object类型时也不可;(可用add(null)?)

编译器必须能够确信通配符表达的是单个、确定的类型。如下情况不可:

ArrayList<Pair<T>> 永远不能捕获ArrayList<Pair<?>>中的通配符。

因为数组可保存多个Pair<?>,而不同的?使Pair<?>代表不同的类型。

参考资料:

《Java核心技术-卷一

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值