文章首发于 【博客园 - 陈树义】 ,点击跳转到原文 《大白话说 Java 泛型(一):入门,原理,使用》
远在 JDK 1.4 版本的时候,那时候是没有泛型的概念的.当时 Java 程序员们写集合类的代码都是类似于下面这样:
在代码中声明一个集合,我们可以往集合中放入各种各样的数据,而在取出来的时候就进行强制类型转换.
List list = new ArrayList();
list.add("www.cnblogs.com");
list.add(23);
String name = (String) list.get(0);
Integer number = (Integer) list.get(1);
但其实这样的代码存在一定隐患,因为可能过了不久我们就会忘记到底我们存放的 list 里面到底第几个是 String,第几个是 Integer 了.这样就会出现下面这样的情况:
上面的代码在运行时会发生强制类型转换异常.这是因为我们在存入的时候,第二个是一个 Integer 类型,但是取出来的时候却将其强制转换为 String 类型了.
List list = new ArrayList();
list.add("www.cnblogs.com");
list.add(23);
String name = (String)list.get(0);
String number = (String)list.get(1); //ClassCastException
泛型的诞生
Sun 公司为了使 Java 语言更加安全,减少运行时异常的发生.于是在 JDK 1.5 之后推出了泛型的概念.
于是在 JDK 1.5 之后,我们如果使用集合来书写代码,可以使用下面这种形式:
泛型就是将类型参数化,其在编译时才确定具体的参数.在上面这个例子中,这个具体的类型就是 String.可以看到我们在创建 List 集合的时候指定了 String 类型,这就意味着我们只能往 List 集合中存放 String 类型的数据.而当我们指定泛型之后,我们去取出数据后就不再需要进行强制类型转换了,这样就减少了发生强制类型转换的风险.
List < String > list = new ArrayList();
list.add("www.cnblogs.com");
list.add("www.cnblogs.com/chanshuyi");
String cnBlogs = list.get(0);
String mywebSite = list.get(1);
上面我们通过两个很简单的例子知道了为什么要有泛型,以及泛型最简单的使用.下面我们通过一个面试中常见的例子来看一下泛型的本质是什么.
泛型的本质
在继续往下看之前,先想一想,这道题输出的结果是什么?
ArrayList < String > a = new ArrayList < String > ();
ArrayList < Integer > b = new ArrayList < Integer > ();
Class c1 = a.getClass();
Class c2 = b.getClass();
System.out.println(c1 == c2);
是 true 还是 false ?
这道题输出的结果是 true.因为无论对于 ArrayList 还是 ArrayList,它们的 Class 类型都是一直的,都是 ArrayList.class.
那它们声明时指定的 String 和 Integer 到底体现在哪里呢?
答案是体现在类编译的时候.
当 JVM 进行类编译时,会进行泛型检查,如果一个集合被声明为 String 类型,那么它往该集合存取数据的时候就会对数据进行判断,从而避免存入或取出错误的数据.
也就是说:泛型只存在于编译阶段,而不存在于运行阶段.在编译后的 class 文件中,是没有泛型这个概念的.
上面我们只是说了泛型在集合中的使用方式,但其实泛型的应用范围不仅仅只是集合,还包括类,方法,Map 接口等等.
泛型的应用还广泛存在于下面几种情形:泛型类,泛型方法,泛型集合.
泛型类
泛型类一般使用字母 T 作为泛型的标志.
使用:
public class GenericClass < T > {
private T object;
public T getObject() {
return object;
}
public void setObject(T object) {
this.object = object;
}
}
除了使用 T 作为泛型类的标志之外,在需要使用 Map 的类中,通常使用 K V 两个字母表示 Key Value 对应的类型.
public static void main(String[] args) {
GenericClass < Integer > integerGenericClass = new GenericClass < >(100);
integerGenericClass.showType();
GenericClass < String > stringGenericClass = new GenericClass < >("www.cnblogs.com/chanshuyi");
stringGenericClass.showType();
}
使用:
public class GenericMap<K, V> {
private K key;
private V value;
public void put(K key, V value) {
this.key = key;
this.value = value;
}
}
泛型方法
public static void main(String[] args) {
GenericMap<Integer, String> team = new GenericMap<>();
team.put(1, "YaoMin");
team.put(2, "Me");
GenericMap<String, Integer> score = new GenericMap<>();
score.put("YaoMin", 88);
score.put("Me", 80);
}
泛型方法一般使用字母 T 作为泛型的标志.
使用:
public class GenericMethod {
public static <T> T getObject(Class<T> clz) throws InstantiationException, IllegalAccessException{
T t = clz.newInstance();
return t;
}
}
总结
public static void main(String[] args) throws Exception {
GenericMethod genericMethod = getObject(GenericMethod.class);
System.out.println("Class:" + genericMethod.getClass().getName());
}
我们通过一个简单的例子理解了泛型诞生的缘由,之后又通过一个常见的面试题明白了泛型只存在于编译期间的本质,最后介绍了常见的泛型使用场景.
下篇文章,我们将介绍泛型中通配符的使用.
文章首发于 【博客园 - 陈树义】 ,点击跳转到原文 《大白话说 Java 泛型(一):入门,原理,使用》
来源: https://www.cnblogs.com/chanshuyi/p/java_advanced_features_the_generic_part_one.html