黑马程序员_集合框架(泛型)

来源:互联网 发布:淘宝自己刷销量 编辑:程序博客网 时间:2024/06/05 01:52
 ----------------- android培训、java培训、期待与您交流! ----------
泛型:jdk1.5版本,出现的技术。是一个安全机制。 

泛型技术的由来:
  • 集合中可以存储任意类型对象,但是在取出时,如果要使用具体对象的特有方法时,需要进行向下转型,
  • 如果存储的对象类型不一致,在转型过程中就会出现ClassCastException异常。
  • 这样就给程序带来了不安全性。

在jdk1.5以后就有了解决方案。就是泛型技术。
  • 解决方案就是,在存储元素时,就不允许存储不同类型的元素。
  • 存储了就编译失败。 所以就需要在存储元素时,在容器上明确具体的元素类型。这其实和数组定义很像。
好处:
  • 将运行时期的ClassCastException异常转移到了编译事情,进行检查,并以编译失败来体现。 
  • 这样有利于程序员尽早解决问题。 避免了向下转型(强转)的麻烦
什么时候写泛型呢?
  • 先简单理解:只要在使用类或者接口时,该类或者接口在api文当描述时都带着<>就需要在使用时,定义泛型。
  • 其实,泛型无非就是通过<>定义了一个形式参数。专门用于接收具体的引用类型。
  • 在使用时,一定要传递对应的实际参数类型。集合中泛型的应用特别多见。
泛型的擦除:
  • 泛型技术是用于编译时期的技术,编译器会按照<>中的指定类型对元素进行检查,
  • 检查不匹配,就编译失败,匹配,就编译通过,通过后,生产的class文件中是没有泛型的。这就成为泛型的擦除。
泛型的补偿:
  • 运行时,可以根据具体的元素对象获取其具体的类型。并用该类型对元素进行自动转换。
  • 泛型对对于程序的设计也有一定优化动作。

接下来就有了新的思考方法,这其实源于jdk1.5的泛型技术。
  • 当一个类要操作的引用数据类型不确定的时候,可以将该类型定义一个形参。
  • 用到的这类时,有使用者来通过传递类型参数的形式,来确定要操作的具体的对象类型。
  • 意味着在定义这个类时,需要在类上定义形参。用于接收具体的类型实参。
  • 这就是将泛型定义在类上。这就是泛型类。

什么时候使用泛型类呢?只要类中操作的引用数据类型不确定的时候,就可以定义泛型类
有了泛型类,省去了曾经的强转和类型转换异常的麻烦。
class Util<QQ>{private QQ obj;public void setObject(QQ obj){this.obj = obj;}public QQ getObject(){return obj;}}

定义一个工具类对worker对象也可以对Student对象进行操作,设置和获取。甚至于任意对象。
之所以定义Object类型的对象是因为不能确定要操作什么类型的具体对象。
弊端是要使用对象的特有方法需要向下转型,并且问题发生,会出现的运行时期,而不是编译时期,不已解决。

class Tool{private Object obj;public void setObject(Object obj){this.obj = obj;}public Object getObject(){return obj;}}

自定义泛型和方法演示代码:
package com.itheima.collection;public class GenericDemo{public static void main(String[] args){GenDemo<String> str = new GenDemo<String>();str.speak("hello,there!");str.speakOther(new Integer(5));GenDemo.hello("thanks!");GenDemo<Integer> in = new GenDemo<Integer>();in.speak(new Integer(6));in.speakOther(new Integer(9));in.speakOther("Strings also can");}}// 自定义泛型类class GenDemo<E>{public void speak(E e){System.out.println("说了什么?"+e.toString());}// 这个speakOther方法,要操作的类型不确定的,但是一不一定和调用该方法的对象指定的类型一致。// 故使用泛型方法public <F> void speakOther(F f){System.out.println("说点不同类型的:"+f.toString());}// 静态方法不能访问类上定义的泛型,如果需要泛型,该泛型只能定义在方法上。public static <S> void hello(S s){System.out.println("静态方法说:"+s.toString());}}
运行结果如下:
说了什么?hello,there!说点不同类型的:5静态方法说:thanks!说了什么?6说点不同类型的:9说点不同类型的:Strings also can

泛型的通配符:?
  • 当操作的不同容器中的类型都不确定的时候,而且使用的都是元素从Object类中继承的方法。
  • 这时泛型就用通配符?来表示即可。
  • 我们就通过Collection中的containsAll方法来对 ? 应用进行一下 体现。 
  • boolean  containsAll(Collection<?>)
  • 注意:如果我们要定义这样类似的方法怎么定义呢?
interface Collection<E>{public boolean add(E e);public boolean containsAll(Collection<?> coll);}Collection<String> c1 = new ArrayList<String>();c1.add("abc1");c1.add("abc2");Collection<String> c2 = new ArrayList<String>();c2.add("abc1");c1.containsAll(c2);//这是必须可以的。Collection<Integer> c3 = new ArrayList<Integer>();c3.add(5);c1.containsAll(c3);//注意:containsAll方法使用的内部原理就是通过元素的equals进行相同的判断。//equals方法的参数是Object.所以可以接受任意类型的对象。意味着: "abc".equals(new Integer(5));
演示代码:
package com.itheima.collection;import java.util.ArrayList;import java.util.Collection;import java.util.Iterator;public class GenericDemo2{public static void main(String[] args){ArrayList<String> als = new ArrayList<String>();als.add("als1");als.add("als2");als.add("als3");//System.out.println(als);ArrayList<Integer> ali = new ArrayList<Integer>();ali.add(3);ali.add(4);ali.add(7);//System.out.println(ali);printColl(als);printColl(ali);}public static void printColl(Collection<?> al){for (Iterator<?> it = al.iterator();it.hasNext();){System.out.println(it.next().toString());}}}

泛型的限定:
  • 明确具体类型代表一个类型。
  • 明确?代表所有类型。
  • 能不能对操作的类型限制在一个范围之内呢?
  • 比如:定义一个功能,只操作Person类型或者Person的子类型。 
  • 这时可以用 ? extends E:接收E类型或者E的子类型。这就是上限。 
  • 下限:? super E: 接收E类型或者E的父类型。
  • 什么时候使用上限呢?一般情况下,只要是往容器中添加元素时,使用上限。  ? extends E
  • 什么时候使用下限呢?一般清况下,只要是从容器中取出元素时,是用下限。 ? super E
//演示下限。
private static void superEDemo() {TreeSet<Student> ts = new TreeSet<Student>(new CompByName());ts.add(new Student("lisi1",21));ts.add(new Student("lisi0",24));ts.add(new Student("lisi2",22));Iterator<Student> it = ts.iterator();while(it.hasNext(){Person p = it.next();System.out.println(p.getName()+":::student:::"+p.getAge());}TreeSet<Worker> ts1 = new TreeSet<Worker>(new CompByName());ts1.add(new Worker("wangwu8",21));ts1.add(new Worker("wangwu1",24));ts1.add(new Worker("wangwu5",22));Iterator<Worker> it1 = ts1.iterator();while(it1.hasNext()){Person p = it1.next();System.out.println(p.getName()+":::worker:::"+p.getAge());}}

//演示上限。
private static void extendsEDemo() {Collection<Student> coll = new ArrayList<Student>();coll.add(new Student("lisi1",21));coll.add(new Student("lisi0",24));coll.add(new Student("lisi2",22));TreeSet<Person> ts = new TreeSet<Person>(coll);//将coll中的元素存储到TreeSet集合。ts.add(new Person("wangwu",23));Iterator<Person> it = ts.iterator();while(it.hasNext()){Person p = it.next();System.out.println(p.getName()+"::::::"+p.getAge());}}

  • 姓名排序。比较器是将容器中的元素取出来进行比较、 
  • 发现,如果对学生或者工人都进行姓名的排序,用的都是父类Person中的内容。
  • 不同的子类型需要定义不同的比较器就很麻烦,复用性差。
  • 定义Person类型的比较器行不?
  • 比较器是将容器中的元素取出来进行比较。所以无论你的元素是Student类型还是Worker类型。;
  • 我都可以用Person类型来接收。而Person类型是两者父类型。 
class CompByName implements Comparator<Person>{public int compare(Person o1, Person o2) {int temp = o1.getName().compareTo(o2.getName());return temp==0? o1.getAge()-o2.getAge():temp;}}

jdk1.5以后 出现的新方式。这种升级就是简化书写。
Collection有了一个父接口,Iterable
该接口封装了iterator方法,同时提供了一个新的语句。foreach语句。
格式:
for(变量 : Collection集合or数组)
{
}
  • foreach循环简化了迭代器。迭代器还用吗?用,因为迭代过程中还可以remove().一般只对基本遍历简化使用。
  • foreach循环特点:必须明确被遍历的目标。没有目标没用。目的只能是数组或者Collection集合。如果要对数组的中的元素进行特定操作时,建议传统for循环,通过角标完成。 

ArrayList<String> al = new ArrayList<String>();al.add("abc1");al.add("abc2");al.add("abc3");al.add("abc5");for(String s : al){System.out.println(s);}int[] arr = {7,23,1,67,90,8};for(int i : arr)//只为遍历元素,无法操作角标。 {System.out.println("i="+i);}



原创粉丝点击