泛型 (Generic)
泛型 (Generic),即“参数化类型”,就是允许在定义类、接口、方法时使用类型形参,这个类型形参将在使用时(声明变量、创建对象、调用方法)传入实际的类型参数(类型实参)。
好处
泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,可以提高代码的重用率。
规则
1. 使用简练的名字作为类型形参的名字,最好为单个的大写字母,比如 T ;
2. 如果一个泛型类有泛型方法,对于它们的类型形参来说,应避免使用相同的名字;
3. 泛型的类型实参只能是类类型,不能是基本数据类型。
“菱形”语法
在 Java 7 之前,如果使用带泛型的接口、类定义变量时,调用构造器创建对象时,构造器后面必须带泛型,如: List<String> list = new ArrayList<String>();
而从 Java 7 开始,Java 允许在构造器后面不带完整的泛型信息,只需给出一对尖括号即可<>,如:List<String> list = new ArrayList<>();
定义泛型类
具体例子:
上面定义了一个带泛型声明的 Person<T> 类,其有一个形式参数为泛型的构造器和一个返回值类型为泛型的 getInfo() 方法,使用 Person<T> 类时就可以为 T 类型形参传入实际的类型参数。
运行结果:
结果分析:
第27行代码的返回结果为 true,这是因为不管为泛型的类型形参传入什么类型实参,它们依然被当成同一个类处理,在内存中也只占一块内存空间。因此,在静态变量、静态方法、静态初始化块的声明和初始化中均不能使用类型形参。另外,instanceof 运算符后也不能使用泛型类。
定义泛型接口
定义泛型接口与定义泛型类道理相同。
定义泛型方法 (Generic Method)
1. 泛型方法,即在声明方法时定义一个或多个类型形参;
2. 语法格式为:
修饰符 <T, S> 返回值类型 方法名 (参数列表) {}
3. 泛型方法中定义的类型形参只能在该方法里使用。
类型通配符
1. 类型通配符是一个问号 ? , 将问号作为类型实参传给泛型类或泛型接口,如:List<?> (意思是元素类型未知的 List)
2. 在 Java 的集合框架中,对于参数值是未知类型的容器类,只能读取其中元素,不能向其中添加元素,因为编译器无法确定添加的元素的类型和集合中的类型是否兼容,但 null 例外,因为它是所有引用类型的实例。
设定类型通配符的上限
1. 当使用 List<?> 这种形式时,表明这个 List 集合可以是任何泛型 List 的父类,如果我们只希望这个 List 集合代表某一类型的父类,可以考虑设定类型通配符的上限。
2. 设定类型通配符上限的格式为:List<? extends Xxxx>
注:Xxxx表示一个类,此处的 ? 表示未知类型,但是此处的未知类型一定是 Xxxx 的子类或它本身,Xxxx 称为这个通配符的上限(upper bound).
3. 由于编译器无法确定这个受限制的通配符的具体类型,所以不能把 Xxxx 对象及其子类的对象加入到这个泛型集合中,只能读取。
设定类型通配符的下限
设定类型通配符上限的格式为:List<? super Xxxx>
注:Xxxx表示一个类,此处的 ? 表示未知类型,但是此处的未知类型一定是 Xxxx 的父类或它本身,Xxxx 称为这个通配符的下限(lower bound).
设定类型形参的上限
1. 同设定类型通配符的上限一样,Java运行设定类型形参的上限,用于表示传给该类型形参的实际类型是该上限类型或其子类,如:
public void drawAll(List<? extends Shape> shapes){}
注:问号 ? 表示 Shape 的未知子类,编译器无法确定这个类型是什么,所以无法把任何对象添加到这种集合中
2. 当为需要类型形参设定多个上限时,类上限必须位于第一位,并且最多只有一个,而接口上限数量则没有限制,如:
public class Student<? extends Person & Serializable>{}
参考资料: