一、泛型的概念
泛型是 Java SE5 出现的新特性,泛型的本质是类型参数化或参数化类型,在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型。
二、泛型的意义
一般的类和方法,只能使用具体的类型:要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大。
Java 在引入泛型之前,表示可变对象,通常使用 Object 来实现,但是在进行类型强制转换时存在安全风险。有了泛型后:
- 编译期间确定类型,保证类型安全,放的是什么,取的也是什么,不用担心抛出 ClassCastException 异常。
- 提升可读性,从编码阶段就显式地知道泛型集合、泛型方法等处理的对象类型是什么。
- 泛型合并了同类型的处理代码提高代码的重用率,增加程序的通用灵活性。
举个例子:
public static void method1() { List list = new ArrayList(); List.add(22); List.add("hncboy"); List.add(new Object()); for (Object o : list) { System.out.println(o.getClass()); }}
未使用泛型前,我们对集合可以进行任意类型的 add 操作,遍历结果都被转换成 Object 类型,因为不确定集合里存放的具体类型,输出结果如下所示。
class java.lang.Integerclass java.lang.Stringclass java.lang.Object
采用泛型之后,创建集合对象可以明确的指定类型,在编译期间就确定了该集合存储的类型,存储其他类型的对象编译器会报错。这时遍历集合就可以直接采用明确的 String 类型输出。
public static void method2() { List<String> list = new ArrayList(); list.add("22"); list.add("hncboy"); //list.add(new Object()); 报错 for (String s : arrayList) { System.out.println(s); }}
三、泛型的表示
泛型可以定义在类、接口、方法中,分别表示为泛型类、泛型接口、泛型方法。泛型的使用需要先声明,声明通过<符号>的方式,符号可以任意,编译器通过识别尖括号和尖括号内的字母来解析泛型。泛型的类型只能为类,不能为基本数据类型。尖括号的位置也是固定的,只能在类名之后或方法返回值之前。
一般泛型有约定的符号:E 代表 Element, 通常在集合中使用;T 代表 Type,通常用于表示类;K 代表 Key,V 代表 Value, 通常用于键值对的表示;? 代表泛型通配符。
泛型的表达式有如下几种:
- 普通符号
- 无边界通配符 <?>
- 上界通配符 <? extends E> 类是 E
- 下界通配符 <? super E> 是 E 的父类
四、泛型的使用
4.1 泛型类
将泛型定义在类名后,使得用户在使用该类时,根据不同情况传入不同类型。在类上定义的泛型,在实例方法中可以直接使用,不需要定义,但是静态方法上的泛型需要在静态方法上声明,不能直接使用。举个例子:
public class Test<T> { private T data; public T getData() { return data; } /** 这种写法是错误的,提示 T 未定义 */ /*public static T get() { return null; }*/ /** 正确写法,该方法上的 T 和类上的 T 虽然一样,但是是两个指代,可以完全相同,互不影响 */ public static <T> T get() { return null; } public void setData(T data) { this.data = data; }}
4.2 泛型方法
泛型方法,是在调用方法时指明的具体的泛型类型。虽然类上定义的泛型,实例方法中可以直接使用,但是该方法不属于泛型方法。举个例子:get 方法为泛型方法,而且该程序能编译通过运行,因为尖括号里的每个元素都指代一种未知类型,可以为任何符号,尖括号里的 String 并非 java.lang.String 类型,而是作为泛型标识 ,传入的 first 为 Integer 类型,所以该 String 标识符也指代 Integer 类型,返回值自然也是 Integer 类型。不过,应该也不会用这种泛型符号定义在实际情况中。
public class Test { public static <String, T, Hncboy> String get(String string, Hncboy hncboy) { return string; } public static void main(String[] args) { Integer first = 666; Double second = 888.0; Integer result = get(first, second); System.out.println(result); }}
4.3 泛型通配符
? 为泛型非限定通配符,表示类型未知,不用声明,可以匹配任意的类。该通配符只能读,不能写,且不对返回值进行操作。也可以将非限定通配符出现的地方用普通泛型标识,不过使用通配符更简洁。举个例子:
test1() 是通过通配符来输出集合的每一个元素的,test2() 和 test1() 的作用一样,只不过将通配符用来代替了;test3() 用来演示集合在通配符的情况下写操作,发现编译器报错,int 和 String 都不属于 ? 类型,当然放不进集合,因为所有类都有 null 元素,所以可以放进集合。比如主函数传的是 List,而想要在集合里添加一个 String,这是不可能的;test4() 的写法也是错的,? 是不确定,返回值返回不了;test5() 的用法使用来比较 List