一 概述
- 问题:什么类型的元素都可以存储。导致取出时,如果出现强转就会引发运行时 ClassCastException。JDK1.5以后,出现了解决方案,使用容器时,必须明确容器中元素的类型。
- 泛型的原理:其实就是在操作的元素类型不确定时,通过传递参数的形式来明确类型。
- 泛型的体现就是 <参数类型变量>用于接收具体的实际元素类型
- 泛型技术在集合框架中应用非常广泛,只要记住:在使用类或者接口时,如果接口上有明确<>泛型在使用时,就传递所需的数据类型即可。不传递会出现警告类型不安全提示。
- 泛型的好处:将运行时期的ClassCastException异常转移到编译时期通过编译失败体现。避免了强制转换的麻烦。
- 其实泛型的使用就是往定义了泛型的类或者接口的<>中传递类型参数
二 使用
如果不使用泛型,你在运行时会发生编译异常,我们到运行时你在看到结果。
//创建集合时,直接在集合上明确要存储的元素的类型。 Listlist = new ArrayList (); list.add("abc"); list.add("zzzz");// list.add(6);//只要不是指定的类型对象,编译器检查会 报错。这样将运行时的问题转移到编译时期。 for (Iterator it = list.iterator(); it.hasNext();) {// Object object = (Object) it.next();// System.out.println(object.toString()); //想要打印字符串的长度。 String str = it.next(); System.out.println(str.length()); }
2.1 类中使用
在类中的使用,明确里面的类型,当类型不对时,编译会报错。
1,我们把运行时异常,转到了编译时不通过,
2, 通过会用泛型,也可以减少你不需要转型了。
注意:下面是在类中的使用,以及你在使用栈和队列时也可以明确进栈或入队时你要的数据类型
package cn.itcast.api.a.generic;import java.util.LinkedList;public class GenericDemo2 { /** * @param args */ public static void main(String[] args) { // Tool t = new Tool();// t.setObject(6);// String s = (String)t.getObject();// System.out.println(s); Toolt = new Tool ();// t.setObject(6);//只要类型错误,编译失败。避免了运行时的类型转换异常。 String s = t.getObject();//省去了强转的麻烦。 System.out.println(s); Queue queue = new Queue (); queue.myAdd("abcd1"); queue.myAdd("abcd2"); queue.myAdd("abcd3"); while(!queue.isNull()){ String string = queue.myGet(); System.out.println(string); } }}//jdk1.5有了新技术,泛型,改成如下这样。//类中操作的对象确定不?不确定,用Object,需要转型,运行容易出异常。不爽。//在定义时,就将不确定的对象的类型,定义成参数。由使用该类的调用者来传递对象类型。class Tool { //将泛型定义在类上,泛型类。 private Q object; public Q getObject() { return object; } public void setObject(Q object) { this.object = object; } }class Queue{ //封装了一个链表数据结构。 private LinkedList link; /* * 队列初始化时,对链表对象初始化。 */ Queue(){ link = new LinkedList (); } /** * 队列的添加元素功能。 */ public void myAdd(E obj){ //内部使用的就是链表的方法。 link.addFirst(obj); } /** * 队列的获取方法。 */ public E myGet(){ return link.removeLast(); } /** * 判断队列中元素是否空,没有元素就为true。 */ public boolean isNull(){ return link.isEmpty(); }}/*//定义一个工具对对象进行操作,比如设置和获取。可以对任意对象进行操作。对共性类型Object操作。//定义Object就哦了。但用的时候,因为提升为了Object,想要使用特有内容,需要向下转型。容易引发ClassCastException:class Tool{ private Object object; public Object getObject() { return object; } public void setObject(Object object) { this.object = object; } }*/
2.2 方法中使用
注意:你在方法时使用时,注意调用的方法。
package cn.itcast.api.a.generic;public class GenericDemo3 { /** * @param args */ public static void main(String[] args) { Utilutil = new Util (); util.show("hehe");// util.print(5); Util util2 = new Util (); Util.print(5); util2.show("hehe"); }}class Util { //当方法要操作的类型不确定和类上的泛型不一定一样。这时可以将泛型定义在方法上。 public void show(Q q){ //泛型方法 System.out.println("show:"+q); } public staticvoid print(E e){ //记住:如果方法是静态,还需要使用泛型,那么泛型必须定义在方法上。 System.out.println("print:"+e); } public void method(W w){ }}
调用泛型方法语法格式如下:
说明一下,定义泛型方法时,必须在返回值前边加一个<T>,来声明这是一个泛型方法,持有一个泛型T,然后才可以用泛型T作为方法的返回值。
Class<T>的作用就是指明泛型的具体类型,而Class<T>类型的变量c,可以用来创建泛型类的对象。
为什么要用变量c来创建对象呢?既然是泛型方法,就代表着我们不知道具体的类型是什么,也不知道构造方法如何,因此没有办法去new一个对象,但可以利用变量c的newInstance方法去创建对象,也就是利用反射创建对象。
泛型方法要求的参数是Class<T>类型,而Class.forName()方法的返回值也是Class<T>,因此可以用Class.forName()作为参数。其中,forName()方法中的参数是何种类型,返回的Class<T>就是何种类型。在本例中,forName()方法中传入的是User类的完整路径,因此返回的是Class<User>类型的对象,因此调用泛型方法时,变量c的类型就是Class<User>,因此泛型方法中的泛型T就被指明为User,因此变量obj的类型为User。
当然,泛型方法不是仅仅可以有一个参数Class<T>,可以根据需要添加其他参数。
为什么要使用泛型方法呢?因为泛型类要在实例化的时候就指明类型,如果想换一种类型,不得不重新new一次,可能不够灵活;而泛型方法可以在调用的时候指明类型,更加灵活。
2.3 接口中使用
package cn.itcast.api.a.generic;public class GenericDemo4 { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub new InterImpl().show("hehe"); }}//泛型接口。interface Inter { void show(E e);}/*class InterImpl implements Inter { public void show(String e){}}*/class InterImpl implements Inter { @Override public void show(T e) { } }
三 泛型的通配符和限定
3.1 通配符的使用
当使用泛型类或者接口时,传递的具体的类型不确定,可以通过通配符(?)表示。
使用了这种,你在使用方法时不能使用具体类的方法,你只能使用Object 里面的方法。例如:toStirng
package cn.itcast.api.a.generic;import java.util.ArrayList;import java.util.Collection;import java.util.HashSet;import java.util.Iterator;import java.util.List;import java.util.Set;import cn.itcast.domain.Student;public class GenericDemo5 { /** * @param args */ public static void main(String[] args) { Setlist = new HashSet (); list.add(new Student("lisi1",21)); list.add(new Student("lisi2",22)); list.add(new Student("lisi3",23)); printList(list); List list2 = new ArrayList (); list2.add("lisi11"); list2.add("lisi22"); list2.add("lisi33"); printList(list2); } /* * 打印集合中的元素。 * * 当使用泛型类或者接口时,传递的具体的类型不确定,可以通过通配符(?)表示。 * */ private static void printList(Collection list2) { for (Iterator it = list2.iterator(); it.hasNext();) { System.out.println(it.next().toString());// 不能使用具体的方法, } } }
3.2 泛型的限定
打印集合中的元素。
当使用泛型类或者接口时,传递的具体的类型不确定,可以通过通配符(?)表示。 如果想要对被打印的集合中的元素类型进行限制,只在指定的一些类型,进行打印。 使用泛型的限定。 只需要打印学生和工人的集合。找到学生和工人的共性类型Person。 ? extends Person : 接收Person类型或者Person的子类型。 总结: ? super E:接收E类型或者E的父类型。下限。 ? extends E:接收E类型或者E的子类型。上限。
package cn.itcast.api.a.generic;import java.util.ArrayList;import java.util.Collection;import java.util.HashSet;import java.util.Iterator;import java.util.List;import java.util.Set;import cn.itcast.domain.Person;import cn.itcast.domain.Student;import cn.itcast.domain.Worker;public class GenericDemo6 { /** * @param args */ public static void main(String[] args) { Setlist = new HashSet (); list.add(new Student("lisi1",21)); list.add(new Student("lisi2",22)); list.add(new Student("lisi3",23)); printList(list); List list2 = new ArrayList (); list2.add(new Worker("lisi11",21)); list2.add(new Worker("lisi22",22)); list2.add(new Worker("lisi33",23)); printList(list2); } /* * 打印集合中的元素。 * 当使用泛型类或者接口时,传递的具体的类型不确定,可以通过通配符(?)表示。 * 如果想要对被打印的集合中的元素类型进行限制,只在指定的一些类型,进行打印。 * 使用泛型的限定。 * * 只需要打印学生和工人的集合。找到学生和工人的共性类型Person。 * ? extends Person : 接收Person类型或者Person的子类型。 * * 总结: * ? super E:接收E类型或者E的父类型。下限。 * ? extends E:接收E类型或者E的子类型。上限。 */ private static void printList(Collection list2) { for (Iterator it = list2.iterator(); it.hasNext();) { Person p = it.next(); System.out.println(p.getName()); } } }
3.3 泛型在API中的使用
通配符? 在api中的体现。 Collection接口: boolean containsAll(Collection c)
package cn.itcast.api.a.generic;import java.util.ArrayList;import java.util.Collection;public class GenericDemo7 { /** * @param args */ public static void main(String[] args) { /* * 通配符? 在api中的体现。 * * Collection接口: boolean containsAll(Collection c) */ Collectionc1 = new ArrayList (); c1.add("haha"); c1.add("hehe"); Collection c2 = new ArrayList (); c2.add(4); c2.add(5); boolean b = c1.containsAll(c2);//了解 containAll源码内判断是否包含的依据。依据是equals方法。 //public boolean equals(Object obj) "abc".equals(5); System.out.println("b="+b); }}/*模拟containaAll * class Collection * { * public boolean containsAll(Collection c){ * } * } */
3.4 泛型限定在API中的使用
上限
一个容器,你要添加元素,加个Person 是可以的,你如果加student 也应该是可以的。
package cn.itcast.api.a.generic;import java.util.ArrayList;import java.util.Collection;import java.util.Iterator;import java.util.TreeSet;import cn.itcast.domain.Person;import cn.itcast.domain.Student;public class GenericDemo8 { /** * @param args */ public static void main(String[] args) { /* * 泛型的限定在api中的使用。上限的体现。 * TreeSet(Collection c) * * */ //创建一个Collection. Collectionc = new ArrayList (); c.add(new Student("wangcai1",26)); c.add(new Student("wangcai2",29)); //TreeSet集合在创建时,就将c中的存储到Treeset集合。 TreeSet ts = new TreeSet (c); ts.add(new Person("lisi",20)); for (Iterator it = ts.iterator(); it.hasNext();) { Person person = it.next(); System.out.println(person); } }}/* * class TreeSet { * TreeSet(Collection c){} */
下限
创建一个学生对象,想要姓名排序,用comparator。
加入工人,你也想用姓名排序,你也可以在建一个comparator
但是这样代码的复用性就降低了。
可以用Person;这时你应该定义Student的父类。
package cn.itcast.api.a.generic;import java.util.Comparator;import java.util.Iterator;import java.util.TreeSet;import cn.itcast.domain.Person;import cn.itcast.domain.Student;import cn.itcast.domain.Worker;public class GenericDemo9 { /** * @param args */ public static void main(String[] args) { /* * 泛型的限定在api中的使用。下限的体现。 * TreeSet(Comparator comparator) * * */ //创建一个集合存储的是学生对象。想要按照姓名排序。 TreeSetts = new TreeSet (new ComparatorByName()); ts.add(new Student("abc",26)); ts.add(new Student("aaa",29)); ts.add(new Student("lisi",20)); for (Iterator it = ts.iterator(); it.hasNext();) { Student student = it.next(); System.out.println(student); } //让工人按照姓名排序。 TreeSet ts2 = new TreeSet (new ComparatorByName()); ts2.add(new Worker("abc",26)); ts2.add(new Worker("aaa",29)); ts2.add(new Worker("lisi",20)); for (Iterator it = ts2.iterator(); it.hasNext();) { Worker worker = it.next(); System.out.println(worker); } }}class ComparatorByName implements Comparator { @Override public int compare(Person o1, Person o2) { int temp = o1.getName().compareTo(o2.getName()); return temp==0? o1.getAge() - o2.getAge() : temp; } }/* * 以下两个比较器,都是通过姓名排序,就是类型不同,一个是student,一个是worker * 既然使用的都是Person的内容,为什么不定义一个Person的比较器。 *//*//定义一个比较器。class ComparatorByName implements Comparator { @Override public int compare(Student o1, Student o2) { int temp = o1.getName().compareTo(o2.getName()); return temp==0? o1.getAge() - o2.getAge() : temp; } }//定义一个工人的姓名比较器。class ComparatorByWorkerName implements Comparator { @Override public int compare(Worker o1, Worker o2) { int temp = o1.getName().compareTo(o2.getName()); return temp==0? o1.getAge() - o2.getAge() : temp; } }*//* * class TreeSet { * TreeSet(Comparator c){} */
总结:
如是你是存元素时(也可以理解为添加元素),你要用到上限 ? extends Person
如果你从集合中把元素取出来,你要拿个类型去接收,我需要把类型写的大点,用 ? super student
3.5 泛型的细节
下面的写法是不正确的
1 ArrayListal = new ArrayList ();//使用泛型,要保证左右类型一致。 2 ArrayList al = new ArrayList
4 Listlist = new ArrayList ();// show(list); } public static void show(List list){ // List list =new ArrayList }
四 泛型限定的应用
获取集合中中元素的最大值
下面是不加泛型的结果
package test;import java.util.ArrayList;import java.util.Collection;import java.util.Iterator;class test { public static void main(String[] args) { Collectioncoll = new ArrayList (); coll.add(new Student("xioming1", 21)); coll.add(new Student("xioming2", 26)); coll.add(new Student("xioming3", 27)); coll.add(new Student("xioming4", 22)); Student stu = getMax(coll); System.out.println(stu); } public static Student getMax(Collection coll) { // 定义变量 Iterator it = coll.iterator(); Student max = it.next(); // 遍历 while (it.hasNext()) { Student temp = it.next(); if (temp.compareTo(max) > 0) { max = temp; } } return max; }}
这个功能虽然实现了,但是有局限性。因为这个功能只能对存储了student对象的集合进行最大值的获取
我们需要扩大范围,使用泛型的限定
package test;import java.util.ArrayList;import java.util.Collection;import java.util.Iterator;class test { public static void main(String[] args) { Collectioncoll = new ArrayList (); coll.add(new Student("xioming1", 21)); coll.add(new Student("xioming2", 26)); coll.add(new Student("xioming3", 27)); coll.add(new Student("xioming4", 22)); Student stu = getMax(coll); System.out.println(stu); } public static > T getMax(Collection coll) { // 定义变量 Iterator it = coll.iterator(); T max = it.next(); // 遍历 while (it.hasNext()) { T o = it.next(); if (o.compareTo(max) > 0) { max = o; } } return max; }}
五 工具类
1,为了解决集合的更多需求。集合框架提供了Collections和Arrays两个工具类,方法都是静态的。
2,Collections是用于操作集合的工具类。 常见方法:对List集合排序,二分查找,对Collection集合进行最值获取。 对排序进行逆序,将非同步的集合转成同步的集合。 3,Arrays对数组操作的工具类: 常见方法:对数组排序,二分查找,数组复制,将数组转成字符串等。 4,数组集合互转。 4.1将数组转成集合 Arrays.asList方法。 目的:使用集合的方法操作数组元素。 注意:不要使用集合的增删方法。因为数组转成List集合后长度是固定的。 转成集合的数组中存储的元素最好是对象,如果是基本数据类型,会将这个数组作为元素存储到集合中。 4.2 集合转成数组。 Collection接口中的toArray方法。 目的:限定对元素的增删,长度的改变。5.1 Collections
1,获取Collection最值。
2,对List集合排序,也可以二分查找。
3,对排序逆序。
4,可以将非同步的集合转成同步的集合
package cn.itcast.api.b.tools;import java.util.ArrayList;import java.util.Collection;import java.util.Collections;import java.util.Comparator;import java.util.List;import cn.itcast.api.c.comparator.ComparatorByLength;public class CollectionsDemo { /** * @param args */ public static void main(String[] args) { /* * Collections: 集合框架中的用于操作集合对象 工具类。 * 都是静态的工具方法。 * 1,获取Collection最值。 * 2,对List集合排序,也可以二分查找。 * 3,对排序逆序。 * 4,可以将非同步的集合转成同步的集合。 * Xxx synchronizedXxx(Xxx) List synchronizedList(List) */ System.out.println("---------获取最值---------------"); Collectionc = new ArrayList (); c.add("haha"); c.add("zz"); c.add("xixii"); c.add("abc"); String max = Collections.max(c,new ComparatorByLength()); System.out.println("max="+max); System.out.println("-----------排序-------------"); List list = new ArrayList (); list.add("hahaha"); list.add("abc"); list.add("xiix"); list.add("z"); list.add("java"); Collections.sort(list,Collections.reverseOrder()); System.out.println(list); System.out.println("------------------------"); System.out.println("------------------------"); }}
5.2 Arrays
工具类-集合和数组的互转
Arrays:用于操作数组的工具类。
类中定义的都是静态工具方法
1,对数组排序。
2,二分查找。
3,数组复制。
4,对两个数组进行元素的比较,判断两个数组是否相同。
5,将数组转成字符串。
package cn.itcast.api.b.tools;import java.util.ArrayList;import java.util.Arrays;import java.util.Collection;import java.util.List;public class ArraysDemo { /** * @param args */ public static void main(String[] args) { /* * Arrays:用于操作数组的工具类。 * 类中定义的都是静态工具方法 * 1,对数组排序。 * 2,二分查找。 * 3,数组复制。 * 4,对两个数组进行元素的比较,判断两个数组是否相同。 * 5,将数组转成字符串。 */ int[] arr = { 34,21,67}; System.out.println(Arrays.toString(arr)); //将arr转成list集合。?如果数组中存储的是基本数据类型,那么转成集合,数组对象会作为集合中的元素存在。 //数组中元素时引用数据类型时,转成,数组元素会作为集合元素存在。 Listlist1 = Arrays.asList(arr); System.out.println(list1); String[] strs = { "hah","hehe","xixi"}; boolean b = contains(strs,"hehe"); System.out.println(b); //将数组转成list集合。asList /* * 数组转成集合:就为了使用集合的方法操作数组中的元素。 * 但是不要使用增删等改变长度的方法。add remove 发生UnsupportedOperationException */ List list = Arrays.asList(strs); System.out.println(list.contains("hehe")); System.out.println(list.get(2)); System.out.println(list.indexOf("hehe"));// list.add("java");//UnsupportedOperationException 数组长度的固定的,转成List集合长度也是固定的。 //-------------------集合转成数组--------------------- /* * 为什么集合转成数组呢? * 为了限制对元素的增删操作。 */ Collection c = new ArrayList (); c.add("haha1"); c.add("haha2"); c.add("haha3"); c.add("haha4"); /* * 如果传递的数组的长度小于集合的长度,会创建一个同类型的数组长度为集合的长度。 * 如果传递的数组的长度大于了集合的长度,就会使用这个数组,没有存储元素的位置为null。 * 长度最好直接定义为和集合长度一致。 */ String[] str_arr = c.toArray(new String[c.size()]); System.out.println(Arrays.toString(str_arr)); } public static boolean contains(String[] strs,String key) { for (int i = 0; i < strs.length; i++) { if(strs[i].equals(key)){ return true; } } return false; } }
练习题目
1,Collection和Collections的区别?【面试题】
Collection是集合框架的顶层接口。 下面有两个开发中常用的List 和 Set集合。 根据数据结构的不同,也有了很多的具体子类集合对象。Collections:是集合框架中的用于操作集合的工具类。
提供了很多的静态方法:比如:对list排序,二分查找,比如可以获取最值等。 其中一组可以非同步集合转成同步 集合的方法。