java泛型应用是java核心基础之一,从java 5开始引进泛型。如果你曾经使用过java Collection(如List/ Map),那你已经算是接触过泛型了。
一、为什么要使用泛型?
先通过一段小程序来了解:
import java.util.ArrayList;
import java.util.List;
public class GenericTest {
public static void main (String[] args) {
List list = new ArrayList(); // (1)
//List<String> list = new ArrayList<String> (); // (2)
list.add("xiaoming");
list.add("xiaohong");
list.add(100); // (3)
System.out.println();
for (int i = 0; i < list.size(); i++) {
String name = (String) list.get(i); // (4)
System.out.println("#" + i + " name: " + name);
}
}
}
代码中(1) 处创建了 list 对象, 并向其添加两个字符串以及一个Integer元素. List 里默认存储的是Object对象,因此编译可以通过.
(4)处依次取出list 中的每个元素,随后并打印.
运行结果:
#0 name: xiaoming
#1 name: xiaohong
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at GenericTest.main(GenericTest.java:15)
可见, 运行时,在取出第三个元素100 时,抛出类型转换错误,即把Integer 类型强制转换成String.
这是因为在(1) 处定义的List 没有限定其类型,导致 list.get(int i) 取出的元素也是Object类型,强制转换为String,忽略了 list[2] 存储的是Integer, 在运行时其也是Integer类型。
那有没有办法确保list存储的是确定的同一类型,以保证在编译阶段就可以检查,避免在运行时刻抛出类型转换异常?
答案就是使用泛型,限定list 对象所能存储的类型.
二、什么是泛型
泛型,即"参数化"类型. 对于泛型类,可以简单地理解为一个类中有不确定的类型,需在创建该类的对象时为其指定确定的类型.
因而,在编译阶段的时候,可以做类型检查.
类比函数中的实参(注意非形参)
同样是上面的代码,注释 (1) 处, 使用(2) 创建List 对象。 则可以发现在编译阶段(3)处会有编译错误如下:
The method add(int, String) in the type List<String> is not applicable for the arguments (int)
即你定义的是 List <String> , 已经限定了其只能容纳String类型的元素.
在(4) 处无需再做类型转换, 可改为: String name = list.get(i); // (4)
因此,泛型可以在提供代码复用性的同时,提供类型检查,减少了数据的类型转换,从而保证了类型安全。
三、泛型的命名规范
K,V — Key,Value,代表Map的键值对
N — Number,数字
T — Type,类型,如String,Integer等等
S,U,V etc. - 2nd, 3rd, 4th 类型,和T的用法一样
四、泛型的种类?
泛型,可以分为泛型类、泛型接口、泛型方法,
再通过一个简单的例子加深对泛型的理解
import java.util.ArrayList;
import java.util.List;
public class GenericTest2 {
public static void main (String[] args) {
Information<String> name = new Information<String> ("Petter");
Information<Integer> age = new Information<Integer> (10);
System.out.println( "name.getClass(): " + name.getClass());
System.out.println( "age.getClass(): " + age.getClass());
System.out.println( "name.getClass() == age.getClass() : " + (name.getClass() == age.getClass()));
}
}
class Information<T> {
private T data;
public Information() {
}
public Information(T data) {
this.data = data;
}
public T getData(){
return data;
}
}
定义了一个Information 泛型类,<T> 表示其拥有 “不定”类型T, 在创建该类的对象时,需要传递确定的"类型实参", 如String、Integer等基本类型,以及自定义的类型.
其成员变量data 是T类型的,getData() 则为泛型方法, 返回的类型是T类型。
在Main 函数中创建了两个对象, 分别传递String/Integer 作为类型实参, 并传递对应类型的data 值。
然后打印两个的类名, 可以看出都为Information, 即实际上在内存中仍为同一类型。可以理解为,在逻辑上是不同的类型,因为其拥有不同类型的T.
关于泛型接口,后续再谈.