【Java的集合框架工具类 泛型 30】
一、泛型:jdk1.5(或者5.0)出现的安全机制。
好处:
1)、将运行时期出现的问题ClassCastException转到编译时期,泛型技术是给编译器使用的技术,用于编译时期。确保了类型的安全
2)、避免了强制类型转换的麻烦
1、<>什么时候用?
当操作的引用数据类型确定的时候。就是用<>。将要操作的引用数据类型传入即可。其实<>就是一个用于接收具体引用数据类型的参数范围。
在程序中,只要用到了带有<>的类或者接口,就要明确传入具体的引用数据类型。
2、泛型的补偿擦除:运行时,会将泛型去掉,生成的class文件中是不带泛型的,这个称为泛型的擦除,为什么擦除呢?因为为了兼容运行的类加载器。
3、泛型的补偿:在运行时,通过获取元素的类型进行转换动作,不用使用者在强制转换了
--->在程序中,只要用到了带有<>的类或者接口,就要明确传入具体的引用数据类型。
--->泛型不可以往里面写基本数据类型:如:<int> 但是可以写成<Integer>
举例1:
package com.JavaStudy01.Generic; import java.util.ArrayList; import java.util.Iterator; /** * @Author wufq * @Date 2020/8/10 09:57 */ public class GenericDemo01 { public static void main(String[] args){ //确定引用类型是String类型,那么在集合内只能存储String类型的数据, // 即:泛指是确定了引用类型(如:String),然后存储的也是特定的类型(如:String) ArrayList<String> al = new ArrayList<>(); al.add("123"); al.add("456"); // al.add(123);只能存储String类型,不能存其他的类型,因为对ArrayList做了泛指 Iterator<String> it = al.iterator(); while (it.hasNext()){ String str = it.next(); System.out.println(str); } } }
举例2:自定义类型的比较(通过年龄和姓名)
----->通过年龄排序:Personew类<-----------
package com.JavaStudy01.Bean; /** * @Author wufq * @Date 2020/8/10 11:03 */ //Comparable<T>接口用于实现对象的比较,比较时指定了对象则可实现指定对象的比较,如果不指定实现的是Object对象的比较 public class Personew implements Comparable<Personew>{ private String name; private int age; public Personew() { super(); } public Personew(String name, int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public int compareTo(Personew per) { int temp = this.age-per.age; return temp==0?this.name.compareTo(per.name):temp; } /* public boolean equals(Object obj){} 这个才是方法覆盖,如果是 public boolean equals(Person p){} 不是方法覆盖,因为equals是Object的方法,所以方法覆盖时参数”Object obj“不能修改。 */ //Comparable<Personew>泛指以后,IDEA直接用快捷方式:command+N来重写equals和hashCode @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Personew personew = (Personew) o; if (age != personew.age) return false; return name != null ? name.equals(personew.name) : personew.name == null; } @Override public int hashCode() { int result = name != null ? name.hashCode() : 0; result = 31 * result + age; return result; } }
----->通过姓名排序:原始Personew类<-----------
package com.JavaStudy01.Bean; /** * @Author wufq * @Date 2020/8/10 11:03 */ //Comparable<T>接口用于实现对象的比较,比较时指定了对象则可实现指定对象的比较,如果不指定实现的是Object对象的比较 public class Personew { private String name; private int age; public Personew() { super(); } public Personew(String name, int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
----->通过姓名排序:ComparatorByNamePers类实现姓名排序<-----------
package com.JavaStudy01.Bean; import java.util.Comparator; /** * @Author wufq * @Date 2020/8/10 14:51 */ public class ComparatorByNamePers implements Comparator<Personew>{ @Override public int compare(Personew o1, Personew o2) { int temp = o1.getName().compareTo(o2.getName()); return temp==0?o1.getAge()-o2.getAge():temp; } }
----->测试类:GenericDemo02类<-----------
package com.JavaStudy01.Generic; import com.JavaStudy01.Bean.ComparatorByNamePers; import com.JavaStudy01.Bean.Personew; import java.util.Iterator; import java.util.TreeSet; /** * @Author wufq * @Date 2020/8/10 11:05 */ public class GenericDemo02 { public static void main(String[] args){ //通过age进行比较 TreeSet<Personew> ts = new TreeSet<>(); ts.add(new Personew("lisi1",21)); ts.add(new Personew("lisi2",22)); ts.add(new Personew("lisi10",28)); ts.add(new Personew("lisi8",10)); Iterator<Personew> it = ts.iterator(); while (it.hasNext()){ Personew p = it.next(); System.out.println(p.getName()+"::"+p.getAge()); } System.out.println("----------------"); //通过name进行比较 TreeSet<Personew> tsp = new TreeSet<>(new ComparatorByNamePers()); tsp.add(new Personew("lisi1",21)); tsp.add(new Personew("lisi2",22)); tsp.add(new Personew("lisi10",28)); tsp.add(new Personew("lisi8",10)); Iterator<Personew> itp = tsp.iterator(); while (itp.hasNext()){ Personew p = itp.next(); System.out.println(p.getName()+"::"+p.getAge()); } } }
二、泛型类、方法、接口
泛型类:
1、如果一个工具类想对所有类进行操作,那么就可以定义一个Object
package com.JavaStudy01.GenericDefault; /** * @Author wufq * @Date 2020/8/10 16:05 */ public class Tool { private Object object; public Object getObject() { return object; } public void setObject(Object object) { this.object = object; } }
2、用工具类调用学生类,需要往里面存一个学生和取一个学生
package com.JavaStudy01.Generic; import com.JavaStudy01.GenericDefault.Tool; import com.JavaStudy01.GenericUtil.Student; import com.JavaStudy01.GenericUtil.Work; /** * @Author wufq * @Date 2020/8/10 15:24 */ public class GenericDemo03 { public static void main(String[] args){ Tool tool = new Tool(); //往里面存了一个学生 tool.setObject(new Student()); tool.setObject(new Work()); //什么对象都能往里面存,好处是扩展性强了,但是也有弊端那就是取对象时都必须的强转。所以需要改进Tool类 //取出来一个学生,学生存进去以后就意味着向上转型了(即:Student向上转为Object),所以需要强制转换 Student stu= (Student) tool.getObject(); } }
发现:由于工具类里面采用了Object,所以任何对象都可以往里面存,但是的需要强转
3、为了解决这个强转问题,也为了更安全,所以在工具类内引入了泛指类
package com.JavaStudy01.GenericDefault; //使用泛型来接受类中要操作的引用数据类型 // 泛型类。什么时候用?当操作类中的引用类型不确定时,就采用泛型类 public class Tool<QQ>{ private QQ q; public QQ getObject() { return q; } public void setObject(QQ q) { this.q = q; } }
4、使用泛型后的引用
package com.JavaStudy01.Generic; import com.JavaStudy01.GenericDefault.Tool; import com.JavaStudy01.GenericUtil.Student; /** * @Author wufq * @Date 2020/8/10 15:24 */ public class GenericDemo03 { public static void main(String[] args){ // 使用泛型类后的引用 Tool<Student> tool = new Tool<>(); tool.setObject(new Student()); Student stu = tool.getObject();// ---->不需要在进行强转了, } }
泛型方法
将泛型定义在方法上,目的是为了对任何类型进行传入或操作
package com.JavaStudy01.GenericDefault; //使用泛型来接受类中要操作的引用数据类型 // 泛型类。什么时候用?当操作类中的引用类型不确定时,就采用泛型类 public class Tool<QQ>{ private QQ q; public QQ getObject() { return q; } public void setObject(QQ q) { this.q = q; } /*将泛型定义在方法上:目的是可以对任何类型进行传入或操作*/ public <W> void show(W str){ System.out.println(str); } public void print(QQ str){ System.out.println(str); } /* * 当方法静态时,不能访问类上定义的泛型。 * 如果静态方法使用泛型,只需要将泛型定义在方法上 * */ public static <W> void method(W w){ System.out.println(w); } }
package com.JavaStudy01.Generic; import com.JavaStudy01.GenericDefault.Tool; /** * @Author wufq * @Date 2020/8/14 09:41 */ public class GenericDemo04 { public static void main(String[] args){ Tool<String> tool = new Tool<>(); //可以传入任意类型的值 tool.show("123"); tool.show(new Integer(888)); tool.show(1.2); //print方法的参数是泛型类型,同样不能传入任何类型的值 tool.print("567"); // tool.print(new Integer(999)); // //静态方法使用泛型 Tool.method(new Integer(123)); Tool.method("456"); } }
泛型接口
package com.JavaStudy01.Generic; import com.sun.org.apache.xpath.internal.operations.String; /** * @Author wufq * @Date 2020/8/18 16:55 */ public class GenericDemo05 { public static void main(String[] args){ Interclass1<Integer> ic2 = new Interclass1<>(); ic2.method(123); } } interface Inter<T>{ public void method(T t); } //在定义类时不明确类型,但是在方法上是明确的 class Interclass1 <K> implements Inter<K> { @Override public void method(K k) { System.out.println(k); } }
三、泛型限定(上下限定)
可以对类型进行限定:
上限:? extend E:接收E类型或者E的子类型对象。
下限: ? super E:接收E类型或者E的父类型。
1、举例:多个基本类型的集合迭代(上限)
package com.JavaStudy01.Generic; import java.util.*; /** * @Author wufq * @Date 2020/8/19 10:39 * 泛型限定(上限) * 基本类型:多个集合的迭代 */ public class GenericAdvanceDemo01 { public static void main(String[] args){ ArrayList<String> al = new ArrayList<>(); al.add("abc"); al.add("bbb"); LinkedList<String> al2 = new LinkedList<>(); al2.add("abc"); al2.add("bbb"); HashSet<String> al3 = new HashSet<>(); al3.add("abc"); al3.add("bbb"); printCollection(al); printCollection(al2); printCollection(al3); System.out.println("-----------"); printCollection1(al); System.out.println("-----------"); printCollection2(al); } //多个集合,并且每个集合的类型是不一样的,这时候就需要他们的一个父类的集合Collection public static void printCollection(Collection<String> al) { Iterator<String> it = al.iterator(); while (it.hasNext()){ String msg = it.next(); System.out.println(msg); } } //不光集合是不同的,同样集合内的类型也是不同的并且是未知的-->可以用到通配符:? public static void printCollection1(Collection<?> al) { Iterator<?> it = al.iterator(); while (it.hasNext()){ // String msg = it.next(); -->通配符因为不知道是什么类型,所以不能赋值给特定的类型 System.out.println(it.next()); } } //指定了一个具体的类型,那么这个类型就可以被操作。所以和通配符的区别在于:通配符不能进行操作,而指定类型确可以操作 public static <T> void printCollection2(Collection<T> al) { Iterator<T> it = al.iterator(); while (it.hasNext()){ T msg = it.next(); System.out.println(msg); } } }
2、举例:多个自定义类型的集合迭代(上限)
package com.JavaStudy01.Generic; import com.JavaStudy01.Bean.Person; import com.JavaStudy01.GenericUtil.Student; import com.JavaStudy01.GenericUtil.Work; import java.util.*; /** * @Author wufq * @Date 2020/8/19 10:47 * 自定义类型:多个集合的迭代 */ public class GenericAdvanceDemo02 { public static void main(String[] args){ ArrayList<Work> al = new ArrayList<>(); al.add(new Work("张三",21)); al.add(new Work("李四",22)); LinkedList<Student> lk = new LinkedList<>(); lk.add(new Student("张三",21)); lk.add(new Student("李四",22)); //打印容器中的元素 -->但是这个容器有个条件?只打印person的子类 printCollectionAction(al); printCollectionAction(lk); } /*public static void printCollectionAction(Collection< Person> al) (Collection<Person> al = new ArrayList<Work>()) -->其实参数是这样的,引用和实体其实是泛型不匹配 { Iterator<String> it = al.iterator(); while (it.hasNext()){ String msg = it.next(); System.out.println(msg); } }*/ //针对以上的错(仍然无法打印Person的子类),改进:只需要把通配符继承一下Person类 public static void printCollectionAction(Collection<? extends Person> al) { Iterator<? extends Person> it = al.iterator(); while (it.hasNext()){ //用Person类接受,迭代器就可以对Person类里面的数据进行操作 Person p = it.next(); System.out.println(p.getName()+"::"+p.getAge()); } } }
2、举例:多个自定义类型的集合迭代(下限)
package com.JavaStudy01.Generic; import com.JavaStudy01.Bean.Person; import com.JavaStudy01.GenericUtil.Student; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; /** * @Author wufq * @Date 2020/8/19 11:43 * 泛型限定(下限) */ public class GenericAdvanceDemo03 { public static void main(String[] args){ ArrayList<Person> al = new ArrayList<>(); al.add(new Person("张三",21)); al.add(new Person("李四",22)); LinkedList<Student> lk = new LinkedList<>(); lk.add(new Student("张三",21)); lk.add(new Student("李四",22)); //打印容器中的元素 -->但是这个容器有个条件?只打印person的子类 printCollectionAction(al); printCollectionAction(lk); } public static void printCollectionAction(Collection<? super Student> al) { Iterator<? super Student> it = al.iterator(); while (it.hasNext()){ System.out.println(it.next()); } } }
四、泛指上限、下限、通配符体现。
1、泛指上限:一般在存储元素的时候使用上限,因为这样去除都是按照上限类型来运算的。不会出现类型安全隐患
------->addAll(Collection<? extends E> c)<-------
addAll(Collection<? extends E> c) 将指定Collection中的所有元素都添加到此Collection种
package com.JavaStudy01.Generic; import com.JavaStudy01.Bean.Person; import com.JavaStudy01.GenericUtil.Student; import com.JavaStudy01.GenericUtil.Work; import java.util.ArrayList; import java.util.LinkedList; /** * @Author wufq * @Date 2020/8/24 10:49 * 泛指上限高级应用 * 一般在存储元素的时候使用上限,因为这样去除都是按照上限类型来运算的。不会出现类型安全隐患。 * addAll(Collection<? extends E> c) 将指定Collection中的所有元素都添加到此Collection中 */ public class GenericAdvanceDemo04 { public static void main(String[] args){ ArrayList<Person> al = new ArrayList<>(); al.add(new Person("张三",21)); al.add(new Person("李四",22)); LinkedList<Student> lk = new LinkedList<>(); lk.add(new Student("张三1",21)); lk.add(new Student("李四1",22)); ArrayList<Work> al1 = new ArrayList<>(); al1.add(new Work("张三2",21)); al1.add(new Work("李四2",22)); ArrayList al2 = new ArrayList(); al2.add("uuu"); al.addAll(al2); System.out.println(al.toString()); } } class MyCollection<E>{ public void add(E e){ } public void addAll(MyCollection<? extends E> e){ } } ====执行结果==== [张三::21, 李四::22, uuu]