概念
泛型的本质是类型参数化,解决不确定具体对象类型的问题.
泛型可以定义在类,接口,方法中,编译器通过识别尖括号和尖括号内的字母来解析泛型.在泛型定义时,约定俗成的符号包括: E代表Element,用于集合中的元素,T 代表 the Type of object ,表示某个类; K 代表 Key ,V代表Value,用于键值对元素.
解析泛型
public class GennericTest<T> {
static <String,T,Person> String get(String string, Person p){
return string;
}
public static void main (String[] args){
Integer first =2222;
Long second =3333;
//调用上方定义的get方法
Integer result = get(first,second);
}
}
以上代码编译正确且可以正常运行,get()是一个泛型方法,first并非是java.lang.String 类型,而是泛型标识,second值代Person.
get()中其他没有被用到的泛型符号并不会导致编译出错,类名后的T与尖括号内的T相同也是合法的,当然在实际应用时,并不会存在这样的定义方式,这里只是期望能够对以下几点加深理解:
- 尖括号里的每个元素都指代一种未知类型.(String出现在尖括号里,它就不是java.lang.String,而仅仅是一个代号)
- 泛型在定义处只具备执行Object方法的能力.(因此在get()内部只能调用Object类中的方法,如toString())
- 尖括号的位置非常讲究,必须在类名之后或方法返回值之前.
- 对于编译之后的字节码指令,其实没有这些花头花脑的方法签名,充分说明了泛型只是一种编写代码时的语法检查.在使用泛型元素时,会执行强制类型转换:
INVOKESTATIC com/person/easy/coding/generic/GenericTest.get(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
CHECKCAST java/lang/Integer
这就是类型檫除,CHECKCAST指令在运行时会检查对象实例的类型是否匹配,如果不匹配,则抛出运行时异常 ClassCastException.
编译后,get() 的参数时两个Object,返回值也是Object,尖括号里很多内容消失了,参数中也没有String和Person两个类型.数据返回给Integer result时,进行了类型强制转换.
泛型优势
泛型就是在编译期增加了一道检查而已,目的是促使程序员在使用泛型时安全放置和使用数据.使用泛型的好处包括:
- 类型安全.放置的是什么,取出来的自然是什么,不用担心会抛出ClassCastException异常.
- 提升可读性. 从编码阶段就显式的知道泛型集合,泛型方法等处理的对象类型是什么.
- 代码重用. 泛型合并了同类型的处理代码,使代码重用度变高.
使用泛型,可以避免使用Object 作为输入和输出,带来的强制转换的风险.只要这种强制转换的风险存在,根据墨菲定律,就一定会发生ClassCastException异常.