背景
在Java推出泛型以前,程序员是可以构建一个元素为Object的集合,该集合是能够存储任意的数据类型对象的,而在使用该集合的过程当中的时候,是需要程序员明确的指定存储每个元素的数据类型,否则是很容易出现ClassCastException异常的。
而Java中的泛型(generics)是jdk5中引入的一个新特性,泛型提供了编译时类型安全监测的机制,该机制允许我们在编译时就监测到非法的类型数据结构。
泛型的本质就是参数化类型,也就是所操作的数据类型被指定为一个参数。
泛型的优点:它使得类型成为安全的,消除了强制类型的转换
泛型类
class 类名称 <泛型标识> {
private 泛型标识 变量名;
}
理论上泛型标识是可以用任何字母标识的,但当然java当中也肯定有自己的规范,所以常用的泛型标识符:T、E、K、V
调用泛型类的语法格式:
类名<具体的数据类型> 对象名 = new 类名<具体的数据类型>( );
在java1.7以后,后面的< >中的具体数据类型就可以省略不写的
类名<具体的数据类型> 对象名 = new 类名< > ( );
泛型派生子类
子类也是泛型类,子类就要和父类的泛型类型要一致
class ChildGeneric<T> extends Generic<T>
当子类不是泛型类的时候,父类就要明确泛型的数据类型
class ChildGeneric extends Generic<String>
泛型类的接口
interface 接口名称 <泛型标识> {
泛型标识 方法名();
}
在使用泛型接口的时候也要注意到
- 要是实现类不是泛型类,接口要明确数据的类型。
- 实现类也是泛型类,实现类要和接口的泛型类型是要一致的。
泛型方法:
修饰符 <T,E,…> 返回值类型 方法名(参数列表){
方法体…
}
public与返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。
只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
与泛型类的定义一样,此处T是可以任意标识的。
在泛型类当中,我们就可以再单独的定义自己的泛型方法,或者在不是泛型类当中定义泛型方法。
在泛型类当中,泛型方法的泛型是独立于泛型类之外的。
● 成员方法的类型就必须和类的方法是一致的。
● 如果static方法要使用泛型能力,就必须使其成为泛型方法。
● 泛型也是支持可变参数的。
注意事项:
- 泛型类,如果没有指定具体的数据类型,那么这个时候,默认的操作数据类型就是Object类。
- 泛型的类型参数只能是类类型,不能是基本的数据类型
- 泛型类型在逻辑上可以看成是多个不同的类型,但实际上都是相同的类型。
- 在泛型里面不能传入基本数据类型,上面也说了,它的默认类型是Object类,所以传入的数据类型也都继承的是Object类。基本数据类型是没有继承Object类型的,所以不能使用。
类型统配符
类型通配符一般都是使用“?”代替具体的类型实参。
所以类型通配符是类型实参,而不是类型形参。
可以接受定义的类型。
泛型通配符上限
类/接口<? extends 实参类型>
要求该泛型的类型,只能是实参类型,或实参类型的子类类型。
使用泛型通配符这种方式在方法体内是不能添加元素的。
泛型通配符下限
类/接口<?super 实参类型>
要求该泛型的类型,只能是实参类型,或实参类型的父类类型。
泛型通配符的下限虽然在方法内可以添加元素,但是它不保证元素数据类型的要求。
泛型擦除
泛型是Java 1.5版本之后才引进的概念,在这之前是玩去没有泛型的,但是,在泛型引入后,它能很好的与之前的代码进行兼容,那是因为,泛型的信息是只存在与代码编译阶段,在进入JVM之前,将与泛型相关的信息会被全部擦除掉,我们就将这个称为类型擦除。
执行下面的代码:
import java.util.ArrayList;
public class Demo04 {
public static void main(String[] args) {
ArrayList<Integer> integers = new ArrayList<>();
ArrayList<String> strings = new ArrayList<>();
System