发布时间:2024-05-27 14:01
作者:软件质量保障
知乎:https://www.zhihu.com/people/iloverain1024
先看下维基百科给出的定义:
Generics are a facility of generic programming that were added to the Java programming language in 2004 within version J2SE 5.0. They were designed to extend Java's type system to allow "a type or method to operate on objects of various types while providing compile-time type safety".
翻译过来就是一句话,即可以理解为泛型就是 参数化 参数类型。
Java 5中添加了泛型,使开发者在使用集合类时提供编译时类型检查并消除ClassCastException异常。集合框架使用泛型来保证类型安全。下面举个例子,看看泛型如何帮助我们安全地使用集合类。
List list = new ArrayList();
list.add("abc");
list.add(new Integer(5)); //OK
for(Object obj : list){
//type casting leading to ClassCastException at runtime
String str=(String) obj;
}
上面的代码写的没毛病,但在运行时就会抛出 ClassCastException,原因是我们试图将List中的 Object 转换为 String,而其中一个元素是 Integer 类型。在 Java 5 引入泛型之后,我们可以声明如下的集合类。
// java 7 ? List
list1 = new ArrayList<>(); List
list1 = new ArrayList (); list1.add("abc");
//list1.add(new Integer(5)); //compiler error
for(String str : list1){
//no type casting needed, avoids ClassCastException
}
在创建List时,指定List中可接受元素类型为String。因此,如果我们尝试在List中添加任何其他类型的对象,程序将抛出编译时错误。在 for 循环中,我们不需要对List中的元素进行类型强制转换,因此泛型的引入消除了代码运行时的 ClassCastException。
泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法
泛型类型用于类的定义中,被称为泛型类。泛型类是在类型上参数化的类或接口。我们使用尖括号 <>来指定类型参数。
为了理解这样做的好处,下面同样从应用的角度举个例子说明下:
public class GenericsTypeOld {
private Object t;
public Object get() {
return t;
}
public void set(Object t) {
this.t = t;
}
public static void main(String args[]){
GenericsTypeOld type = new GenericsTypeOld();
type.set("Pankaj");
String str = (String) type.get(); //type casting, error prone and can cause ClassCastException
}
}
可以注意下,在使用此类时,我们必须使用类型转换,否则它会在运行时报 ClassCastException异常。现在我们将使用泛型重写相同的类,如下所示。
public class GenericsType
{
private T t;
public T get(){
return this.t;
}
public void set(T t1){
this.t=t1;
}
public static void main(String args[]){
GenericsType
type = new GenericsType<>(); type.set("Pankaj"); //valid
GenericsType type1 = new GenericsType(); //raw type
type1.set("Pankaj"); //valid
type1.set(10); //valid and autoboxing support
}
}
注意在 main 方法中使用了 GenericsType 类。我们不需要进行类型转换,因此消除了代码中的ClassCastException。
泛型接口与泛型类的定义及使用基本相同。可以看一个例子:
import java.util.*;
public interface Comparable
{ public int compareTo(T o);
}
我们也可以有多个类型参数,就像在 Map 接口中一样。同样,我们也可以为参数化类型提供参数化值,例如new HashMap
类型命名约定有助于我们轻松理解代码。所以泛型也有自己的表示方式。通常,类型参数名称是单个大写字母,以便与 Java变量区分开来。最常用的表示方式如下:
E – 元素(Java 集合框架广泛使用,例如 ArrayList、Set 等)
K - 键(用于Map)
N - Number
T - 类型
V – 值(用于Map)