泛型类
泛型通配符<? extends T>来接收返回的数据,此写法的泛型集合不能使用 add 方 法,而<? super T>不能使用 get 方法,作为接口调用赋值时易出错。
说明:扩展说一下 PECS(Producer Extends Consumer Super)原则:第一、频繁往外读取内
容的,适合用<? extends T>。第二、经常往里插入的,适合用<? super T>。
泛型类的最基本写法
1 | class 类名称 <泛型标识:可以随便写任意标识号,标识指定的泛型的类型>{ |
一个最普通的泛型类:
1 | //此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型 |
1 | 注意: |
泛型接口
泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中,可以看一个例子:
1 | //定义一个泛型接口 |
当实现泛型接口的类,未传入泛型实参时:
1 | /** |
当实现泛型接口的类,传入泛型实参时:
1 | /** |
泛型方法
1 | 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T这个T可以出现在这个泛型方法的任意位置. |
泛型方法与可变参数
泛型方法和可变参数的例子:
1 | public <T> void printMsg( T... args){ |
静态方法与泛型
静态方法有一种情况需要注意一下,那就是在类中的静态方法使用泛型:==静态方法无法访问类上定义的泛型==;
如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。
即:如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法 。
1 | public class StaticGenerator<T> { |
泛型上下边界
泛型类的例子
1 | public class Generic<T extends Number>{ |
泛型方法的例子:
1 | //在泛型方法中添加上下边界限制的时候,必须在权限声明与返回值之间的<T>上添加上下边界,即在泛型声明的时候添加 |
泛型方法的类型推断
1 | /**不指定泛型的时候*/ |
类型擦除
编译期间,所有的泛型信息都会被擦除,List
原始类型
原始类型就是泛型类型擦除了泛型信息后,在字节码中真正的类型。无论何时定义一个泛型类型,相应的原始类型都会被自动提供。原始类型的名字就是删去类型参数后的泛型类型的类名。擦除类型变量,并替换为限定类型(T为无限定的类型变量,用Object替换)
因为在Pair
如果是Pair
1 | ArrayList<Integer> array=new ArrayList<Integer>(); |
PECS法则
PECS法则:生产者(Producer)使用extends,消费者(Consumer)使用super
1、生产者
如果你需要一个提供E类型元素的集合,使用泛型通配符<? extends E>。它好比一个生产者,可以提供数据。
1 | List<? extends Number> list=new ArrayList(); |
2、消费者
如果你需要一个只能装入E类型元素的集合,使用泛型通配符<? super E>。它好比一个消费者,可以消费你提供的数据。
1 | List<? super Number> list=new ArrayList(); |
3、既是生产者也是消费者
既要存储又要读取,那就别使用泛型通配符。
泛型相关问题
1、泛型类型引用传递问题
在Java中,像下面形式的引用传递是不允许的:
1 | ArrayList<String> arrayList1=new ArrayList<Object>();//编译错误 |
第一种情况,将第一种情况拓展成下面的形式:
1 | ArrayList<Object> arrayList1=new ArrayList<Object>(); |
在第4行代码处,就会有编译错误。那么,先假设它编译没错。那么当我们使用arrayList2引用用get()方法取值的时候,返回的都是String类型的对象,可是它里面实际上已经存放了Object类型的对象,这样,就会有ClassCastException了。
在看第二种情况,将第二种情况拓展成下面的形式:
1 | ArrayList<String> arrayList1=new ArrayList<String>(); |
这样的情况比第一种情况好的多,最起码,用arrayList2取值的时候不会出现ClassCastException,因为是从String转换为Object。可是,这样做有什么意义呢,泛型出现的原因,就是为了解决类型转换的问题。我们使用了泛型,到头来,还是要自己强转,违背了泛型设计的初衷。所以java不允许这么干。再说,如果又用arrayList2往里面add()新的对象,那么到时候取得时候,我怎么知道我取出来的到底是String类型的,还是Object类型的呢?
所以,要格外注意泛型中引用传递问题。
2、泛型类型变量不能是基本数据类型
比如,没有ArrayList
3、运行时类型查询
举个例子:
1 | ArrayList<String> arrayList=new ArrayList<String>(); |
因为类型擦除之后,ArrayList
1 | if( arrayList instanceof ArrayList<String>) |
java限定了这种类型查询的方式,?为通配符,也即非限定符。
1 | if( arrayList instanceof ArrayList<?>) |