一. 泛型概念的提出(为什么需要泛型)?
首先,我们看下下面这段简短的代码:
//import java.util.List; public class GenericTest { public static void main(String[] args) { List list = new ArrayList(); list.add("语文"); list.add("数学"); list.add(100); //编译错误 for (int i = 0; i < list.size(); i++) { String name = (String) list.get(i); // 1 System.out.println("name:" + name); } } }
定义了一个List类型的集合,先向其中加入了两个字符串类型的值,随后加入一个Integer类型的值。这是完全允许的,因为此时list默认的类型为Object类型。在之后的循环中,由于忘记了之前在list中也加入了Integer类型的值或其他编码原因,很容易出现类似于//1中的错误。因为编译阶段正常,而运行时会出现“java.lang.ClassCastException”异常。因此,导致此类错误编码过程中不易发现。
在如上的编码过程中,我们发现主要存在两个问题:
1.当我们将一个对象放入集合中,集合不会记住此对象的类型,当再次从集合中取出此对象时,改对象的编译类型变成了Object类型,但其运行时类型任然为其本身类型。
2.因此,//1处取出集合元素时需要人为的强制类型转化到具体的目标类型,且很容易出现“java.lang.ClassCastException”异常。
那么有没有什么办法可以使集合能够记住集合内元素各类型,且能够达到只要编译时不出现问题,运行时就不会出现“java.lang.ClassCastException”异常呢?答案就是使用泛型。
二.什么是泛型?
泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
看着好像有点复杂,首先我们看下上面那个例子采用泛型的写法。
public static void main(String[] args) { List<String> list = new ArrayList<String>();//2 list.add("语文"); list.add("数学"); list.add("英语"); //list.add(100);//1 编译错误 //输出 for(int i=0 ; i<list.size();i++){ String subjectName = list.get(i);//3 System.out.println("科目名称:"+subjectName); } }
1. 程序//1处试图将一个Integer对象添加到list集合中,程序将无法通过编译,报出:
java.lang.Error: Unresolved compilation problem
因为list只接受String对象作为元素,所以//1处代码在编译时会得到错误提示
2.所以要使用泛型,如程序2处创建一个特殊的List集合:list,这个List集合只能保存字符串对象,不能保存其他类型的对象
创建这种特殊集合的方式是:在集合接口、类后增加尖括号,尖括号里放一个数据类型,即表示这个集合接口、结合类只能保存特定类型的对象
如//2处的类型声明,它指定:list不是一个任意的list,而是一个String类型的list,写作List<String>.我们认为list是带一个类型参数的泛型接口
3.程序在//3处不需要进行强制类型转换,因为list对象可以“记住”它的所有集合元素都是String类型
结合上面的泛型定义,我们知道在List<String>中,String是类型实参,也就是说,相应的List接口中肯定含有类型形参。且get()方法的返回结果也直接是此形参类型(也就是对应的传入的类型实参)。
三、JAVA 7泛型的“棱形”写法
在java 7以前,如果使用带泛型的接口、类定义变量,那么调用构造器创建对象时构造器的后面也必须带泛型,则显得有些多余,例如:
List<String> strList = new ArrayList<String>(); Map<String ,Interger> scores = new HashMap<String ,Integer>();
上面两条语句中的粗体代码部分是完全多余的,在java 7以前这是必需的,不能省略。java 7以后,只要给出一对尖括号(<>)即可,java可以
推断出尖括号里应该是什么泛型信息。即上面两条语句可以改为如下形式:
List<String> strList = new ArrayList<>(); Map<String ,Interger> scores = new HashMap<>();
因而简化代码:
List<String> list = new ArrayList<>();//1 list.add("语文"); list.add("数学"); list.add("英语"); //list.add(100);//2 //输出 for(int i=0 ; i<list.size();i++){ String subjectName = list.get(i);//3 System.out.println("科目名称:"+subjectName); }
Double泛型:
List<Double> list = new ArrayList<>();//2 list.add(10.1); list.add(10.2); list.add(10.3); //list.add(100);//1 //输出 for(int i=0 ; i<list.size();i++){ Double subjectName = list.get(i);//3 System.out.println("科目成绩:"+subjectName); }
键值对:
java中的map其实就是以键值对形式的存放数据的容器,其常用的实现类主要是哈希map
例如:
Map map = new HashMap();
插入元素:map.put("key", obj);
移除元素: map.remove("key");
清空: map.clear();
import java.util.HashMap;
import java.util.Map;
Map<String,Double> list = new HashMap<>();//2 list.put("语文",10.1); list.put("数学",10.1); list.put("英语",100.0); String arr[]={"语文","数学","英语"}; //输出 for(int i=0 ; i<list.size();i++){ Double subjectName = list.get(arr[i]);//必须是键值对 System.out.println("科目成绩:"+subjectName); }