黑马程序员——012——JavaAPI④(集合框架(泛型)、泛型类、泛型方法、泛型限定)
来源:互联网 发布:淘宝会员名怎么更改 编辑:程序博客网 时间:2024/05/20 18:40
泛型
引入:首先我们来看一个实例代码:
———————————————————————————
import java.util.*;class Demo12_1{ public static void main(String[] args) { //定义一个ArrayList容器 ArrayList al = new ArrayList(); al.add("halo1"); al.add("halo2"); al.add("halo3"); al.add(4);//相当于al.add(new Integer(4)) Iterator it = al.iterator(); while(it.hasNext()) { String s = (String)it.next();//抛异常 System.out.println(s); } }}
———————————————————————————
执行结果:在“String s = (String)it.next();”处抛异常;
表现为:编译没问题,运行有问题;
—这就可能使得程序员在开发的时候不能及时的发现问题,因此JDK在1.5版本以后出现了新特性,用于解决安全问题,是一个类型安全机制。
—我们再来回顾一下升级的三个目的:
——①高校;
——②简化书写;
——③安全;
——显然这个泛型是为了安全而来的;
——如果我们用了泛型对以上程序进行改写,我们就能够把问题转移到编译期:
———————————————————————————
import java.util.*;class Demo12_2{ public static void main(String[] args) { //定义一个ArrayList容器 ArrayList<String> al = new ArrayList<String>(); al.add("halo1"); al.add("halo2"); al.add("halo3"); //al.add(4);//抛异常 Iterator<String> it = al.iterator(); while(it.hasNext()) { String s = it.next(); System.out.println(s); } }}
———————————————————————————
这时候程序编译运行都没有问题了,注意一下几个地方的改变:
(图a)
———————————————————————————
而且我们会发现编译的反馈信息也发生了改变:加了泛型之后,不安全的信息也没有了。
泛型好处:
—①将运行时期出现问题ClassCastException,转移到了编译时期,方便于程序员解决问题。让运行事情问题减少,安全;
—②避免了强制转换麻烦;((图a)的箭头处不需要强制转换类型了)
——而1.5以前,1.4…等,程序员需要主观判定里面存什么类型的元素;
———————————————————————————
泛型使用
泛型格式:
—通过<>来定义要操作的引用数据类型;
使用情形:
—通常在集合框架中很常见;只要见到<>就要定义泛型;
——其实<>就是用来接收类型的;
——当使用集合的时候,将集合中要存储的数据类型作为参数传递到<>中即可;
—接口Comparable的compareTo方法可以使用泛型来避免强转;
—接口Comparator的compare方法也可以使用泛型来避免强转;
—Iterator迭代器也可以定义泛型避免强转;
注意:
Object中的equals方法就没有泛型来避免强转了,一定多态,必要的时候一定得强转了;
我们加入泛型在将前面小节中的学生工人的实例重写一下:
—需求:
——工人对象不具备比较性,将若干工人对象存入HashSet集合;
——学生对象具备比较性,将若干学生对象存入TreeSet集合,并且按照年龄小大顺序排序;
———————————————————————————
import java.util.*;class Demo12_3{ public static void main(String[] args) { //定义一个装工人的集合HashSet,并用泛型声明该集合只能够装工人类对象 HashSet<Worker> hs = new HashSet<Worker>(); hs.add(new Worker("工人1",23)); hs.add(new Worker("工人2",25)); hs.add(new Worker("工人1",27));//同姓名不同年龄 hs.add(new Worker("工人4",23));//同年龄不同姓名 System.out.println(hs); //打印结果:[工人4-23, 工人2-25, 工人1-23, 工人1-27] //定义一个装学生的集合TreeSet,并用泛型声明该集合只能够装学生类对象 TreeSet<Student> ts = new TreeSet<Student>(); ts.add(new Student("学生1",23)); ts.add(new Student("学生2",25)); ts.add(new Student("学生1",27));//同姓名不同年龄 ts.add(new Student("学生4",23));//同年龄不同姓名 System.out.println(ts.add(new Student("学生2",25)));//同年龄同姓名,同一个人添加失败 System.out.println(ts); //打印结果:[学生1-23, 学生4-23, 学生2-25, 学生1-27] }}/*工人类*/class Worker{ private String name; private int age; public Worker(String name,int age) { this.name = name; this.age = age; } public String getName() { return this.name; } public int getAge() { return this.age; } public int hashCode() { return this.name.hashCode()+this.age*37; } public boolean equals(Object obj) { if(!(obj instanceof Worker)) throw new RuntimeException("不是Person类型的元素"); Worker w = (Worker)obj; return this.name.equals(w.getName()) && this.age == w.getAge(); } public String toString() { return this.name+"-"+this.age; }}/*学生类*/class Student implements Comparable<Student>//泛型的定义{ private String name; private int age; public Student(String name,int age) { this.name = name; this.age = age; } public String getName() { return this.name; } public int getAge() { return this.age; } public int hashCode() { return this.name.hashCode()+this.age*37; } /*覆盖equals方法*/ public boolean equals(Object obj) { if(!(obj instanceof Student)) throw new RuntimeException("不是Person类型的元素"); //equals方法中必须要强制转型了 Student s = (Student)obj; return this.name.equals(s.getName()) && this.age == s.getAge(); } /*重写接口的compareTo方法, 参数设置成和泛型一致的类型,不需要强转了*/ public int compareTo(Student s) { //按照年龄排序 //包装成Integer类,调用Integer已经实现的compareTo方法 int comResult = new Integer(this.age).compareTo(s.getAge()); if(comResult==0)//年龄一致再用姓名排序 return this.name.compareTo(s.getName()); return comResult; } public String toString() { return this.name+"-"+this.age; }}
———————————————————————————
执行结果:
总结:
—HashSet无序不重复;
—TreeSet可以排序不重复;
———————————————————————————
泛型类
诸如Comparable接口和Comparator接口等,这种类后面是跟着<类型>的类,就是泛型类(接口也是特殊的类)。
—何时定义泛型类:
——当类中要操作的引用数据类型不确定的时候,早期定义Object来完成拓展,有了泛型之后就可以定义泛型来完成扩展了;
——注意只能够是引用类型的,<>中间不能够放基本类型;
——如下面的例子:一个自定义的泛型类
———————————————————————————
class Demo12_4{ public static void main(String[] args) { Utils<String> u = new Utils<String>(); //QQ是什么,由传进去的类型决定,这里就是String u.setObject(new String());//只能够传入String类型的 u.setObject(new Integer());//如果传入String以外类型的会报错 //将问题转移到了编译时期,不再需要使用instanceof判断类型了 }}/*自定义泛型类*/class Utils<QQ>//QQ为泛型类型,只是一个替代单词{ private QQ q; public void setObject(QQ q) { this.q = q; } public QQ getObject() { return q; }}
———————————————————————————
泛型方法
泛型除了定义在类上,还可以定义在方法上,以上的例子Utils的setObject的参数也是泛型,不过并不是我们要讲的,先观察看下面的代码:
———————————————————————————
class Demo12_5{ public static void main(String[] args) { GenericClass1 gc1 = new GenericClass1(); gc1.show("halo");//调用show(String s)方法 gc1.show(1);//调用show(Integer i)方法 GenericClass2<String> gc2 = new GenericClass2<String>(); gc2.show("halo");//调用show方法 GenericClass2<Integer> gc3 = new GenericClass2<Integer>(); //然而这时候泛型已经固定为Integer类型了,不能改变了 gc3.show(1);//调用show方法 }}class GenericClass1{ /* 类上不用泛型,定义show方法, 要求只有一个参数, 并且既能够接收String类型, 也能够接收Integer类型参数 并且打印 */ //不用泛型,参数需要多少个类型就要写多少个show方法 public void show(String s) { System.out.println("show:"+s); } public void show(Integer i) { System.out.println("show:"+i); }}/*定义泛型类*/class GenericClass2<T>{ /* 类上使用泛型,定义show方法, 要求只有一个参数, 并且既能够接收String类型, 也能够接收Integer类型参数 并且打印 */ //使用用泛型,上面的方法都可以删除了 public void show(T t) { System.out.println("show:"+t); }}
———————————————————————————
正如代码中注释所说的,在GenericClass1的时候要想show方法能够接受多种参数,就需要重载出很多的方法;而在GenericClass2的时候想show方法能够接受多种参数,只需在new对象的时候传入一个类型即可。
用简单的话描述一下就是:
—以前在没有泛型的时候,参数不一样需要定义多个重载方法来适配需求:
—而现在不需要了,有了泛型,一个全搞定:
—然而我们也看到了,泛型类一旦建立了对象,那么其泛型的类型就固定不可更改了,那么我们就想如果不在类上定义泛型,而在方法上定义泛型的话,那么是不是就可以建立的对象的时候泛型还是泛型,而不会具体类型呢。我们来看下面代码:
———————————————————————————
class Demo12_6{ public static void main(String[] args) { GenericClass gc = new GenericClass(); gc.show("halo");//调用show(String s)方法 gc.show(1);//调用show(Integer i)方法 }}/*泛型在方法上的类*/class GenericClass{ /* show方法上定义泛型 */ public <T> void show(T t) { System.out.println("show:"+t); }}/*泛型在类上和方法上的类*/class GenericClass1<T>{ /* show方法上定义泛型 */ public void print(T t)//访问类上定义的泛型 { System.out.println("print:"+t); } /* show方法上定义泛型 */ public <Q> void show(Q q) { System.out.println("show:"+q); }}
———————————————————————————
总结:
—①泛型定义在类上不如泛型定义在方法上灵活,因为方法的泛型是根据参数类型而转变的,而类上的泛型则是根据new对象的时候固定下来只有一种了;
—②当然两者不冲突,可以同时定义,如上面的GenericClass1类;
———————————————————————————
既然类上的泛型涉及到需要new对象才能够确定,那么如果有静态方法参数访问到了该泛型,这时候泛型是什么类型还没有固定下来,会如何呢?
—所以我们有了总结③
—③如果是静态方法的参数使用的泛型,那么泛型必须定义在该方法上(因为在内存中静态方法比对象先有);
——书写格式:
———(正确)public static void method(W w){…}
———(错误)public static void method(W w){…}
———记住泛型写在返回类型的前面
———————————————————————————
泛型接口
泛型定义在接口上,如:
————————————————————
interafce Inter<T>{ void show(T t);}class InterImpl implements Inter<String>//泛型固定位String类型{ public void show(String t) { System.out.println("show:"+t); }}class Demo12_7{ public static void main(String[] args) { InterImpl i = new InterImpl(); i.show("haha'); }}
————————————————————
若实现接口的类继续使用泛型,不知道需要什么类型(类型不明确):
————————————————————
class InterImpl<T> implements Inter<T>//泛型确切类型还没固定{ public void show(T t) { System.out.println("show:"+t); }}class Demo12_8{ public static void main(String[] args) { /* InterImpl i = new InterImpl(); i.show("haha'); */ InterImpl<Integer> i = new InterImpl<Integer>(); i.show(5); }}
———————————————————————————
泛型的高级应用(泛型限定)
使用通配符的方式将泛型能够确定的类型限定在一个范围内,通配符也可以理解为占位符。
有3种方式:
—第①种:
import java.util.*;class Demo12_9{ public static void main(String[] args) { TreeSet<Student> ts = new TreeSet<Student>(new Com()); ts.add(new Student("cbc")); ts.add(new Student("obc")); ts.add(new Student("abc")); ts.add(new Student("dbc")); show3(ts); } /* //使用<?>方式 public static void show1(TreeSet<?> ts) { Iterator<?> it = ts.iterator(); while(it.hasNext()) { System.out.println(it.next().getName()); //getName是Person的特有方法 //因为使用了?占位符,因此getName不能够使用了 } } //使用<T>方式 public static <T> void show2(TreeSet<T> ts) { Iterator<T> it = ts.iterator(); while(it.hasNext()) { System.out.println(it.next().getName()); //和上面原因差不多,getName不能够使用了 } } */ public static void show3(TreeSet<? extends Person> ts) { Iterator<? extends Person> it = ts.iterator(); while(it.hasNext()) { //因为做了泛型的上限限定Person的子类, //因此Person体系下的特有方法getName可以使用了 System.out.println(it.next().getName()); } }}/*定义人类*/class Person implements Comparable<Person>{ private String name; public Person(String name) { this.name = name; } public String getName() { return this.name; } public int compareTo(Person p) { return this.name.compareTo(p.getName()); }}/*定义学生类,继承自人类*/class Student extends Person{ public Student(String name) { super(name); }}/*定义人类比较器*/class Com implements Comparator<Person>{ public int compare(Person p1,Person p2) { return p1.getName().compareTo(p2.getName()); }}
———————————————————————————
以上就是关于
import java.util.*;class Demo12_10{ public static void main(String[] args) { //TreeSet<Person> ts = new TreeSet<Person>(new WorkerComp()); //由于比较器限定了Worker类型以及其父类,也就是Person, //所以这个比较器就不能够使用Student的实例的了 //TreeSet<Person> ts = new TreeSet<Person>(new StudentComp()); //由于比较器限定了Student类型以及其父类,也就是Person, //所以这个比较器就不能够使用Worker的实例的了 //使用new PersonComp()作为通用比较器 //而不用每个类都定义一种比较器,应用泛型 //因为TreeSet的构造函数 //TreeSet(Comparator<? super E> comparator) TreeSet<Person> ts1 = new TreeSet<Person>(new PersonComp()); ts1.add(new Student("sabc01")); ts1.add(new Student("sabc04")); ts1.add(new Student("sabc03")); ts1.add(new Student("sabc02")); show(ts1); TreeSet<Person> ts2 = new TreeSet<Person>(new PersonComp()); ts2.add(new Worker("wabc01")); ts2.add(new Worker("wabc04")); ts2.add(new Worker("wabc03")); ts2.add(new Worker("wabc02")); show(ts2); } public static void show(TreeSet<? extends Person> ts) { Iterator<? extends Person> it = ts.iterator(); while(it.hasNext()) { System.out.println(it.next().getName()); } }}/*学生比较器*/class StuComp implements Comparator<Student>{ public int compare(Student s1,Student s2) { return s1.getName().compareTo(s2.getName());//顺序 }}/*工人比较器*/class WorkerComp implements Comparator<Worker>{ public int compare(Worker w1,Worker w2) { return w1.getName().compareTo(w2.getName());//顺序 }}/*用一个比较器代替上面两个比较器*/class PersonComp implements Comparator<Person>{ public int compare(Person p1,Person p2) { return p1.getName().compareTo(p2.getName());//顺序 //return p2.getName().compareTo(p1.getName());//倒序 }}class Person{ private String name; public Person(String name) { this.name = name; } public String getName() { return this.name; }}class Student extends Person{ public Student(String name) { super(name); }}class Worker extends Person{ public Worker(String name) { super(name); }}
———————————————————————————
以上实例就是
- 黑马程序员——012——JavaAPI④(集合框架(泛型)、泛型类、泛型方法、泛型限定)
- 黑马程序员——集合框架(泛型限定)
- 黑马程序员------集合框架(No.4)(泛型、泛型类、泛型方法、泛型接口、泛型限定)
- 黑马程序员--集合框架---泛型限定
- 黑马程序员——第五章 JavaAPI之集合框架
- 黑马程序员——013——JavaAPI⑤(集合框架(Map)、Collections)
- 集合框架——泛型限定
- 黑马程序员——泛型限定
- 黑马程序员——010——JavaAPI②(集合框架(List之ArrayList)、迭代器、枚举)
- 黑马程序员——011——JavaAPI③(集合框架(List之LinkedList)、(Set之HashSet)
- 黑马程序员——泛型、泛型限定
- 黑马程序员——JavaAPI
- 黑马程序员——12JavaAPI集合类
- 黑马程序员——泛型限定,上限,下限
- 黑马程序员—集合框架(1)
- 黑马程序员—集合框架(2)
- 黑马程序员——JAVA基础拾遗之泛型和集合框架(二)
- 黑马程序员——Java集合框架(二)之泛型
- 抽象类和接口详解
- linux多线程
- va_list/va_start/va_arg/va_end深入分析
- hdu2866 数论Prime
- 8.17 问题总结
- 黑马程序员——012——JavaAPI④(集合框架(泛型)、泛型类、泛型方法、泛型限定)
- fputs与fgets
- Servlet和JSP简单理解
- ios通过svg截取头像
- 沙盒认识二
- 实习笔记5
- 理解Fragment生命周期
- DispatcherServlet详解
- FZU 2092 收集水晶 (DFS, 记忆化搜索)