【JAVA之泛型】
一、引例。
1.引例。
假设现在有一个ArrayList的容器,如果不使用泛型约束,则可以向容器中加入各种类型的对象,但是如果取出来的时候只是用一种类型的转换则肯定会抛出ClassCastException异常。
1 package p04.GenerateTypeDemo.Demo01; 2 3 import java.util.ArrayList; 4 import java.util.ListIterator; 5 6 public class Demo01 { 7 public static void main(String args[]) 8 { 9 ArrayList al=new ArrayList(); 10 al.add("abc1"); 11 al.add("abc2"); 12 al.add(1); 13 al.add(2); 14 ListIterator li=al.listIterator(); 15 while(li.hasNext()) 16 { 17 String str=(String)li.next(); 18 System.out.println(str); 19 } 20 } 21 22 }
虽然这个程序在Eclipse中编译时并没有报错,但是运行的时候则会产生ClassCastException异常,这样就产生了潜在的风险。虽然Eclipse没有报错,但是它会使用黄色小叹号提示程序员这样做有潜在的风险。该怎么保证在编译时期就确定是否有风险呢?泛型在JDK1.5之后就为此而产生了。
泛型使用<>来表示,<>里面天上泛型的类型。这样容器就只能接收指定类型的对象了。
更改代码:
1 package p04.GenerateTypeDemo.Demo01; 2 3 import java.util.ArrayList; 4 import java.util.ListIterator; 5 6 public class Demo01 { 7 public static void main(String args[]) 8 { 9 ArrayList<String>al=new ArrayList<String>(); 10 al.add("abc1"); 11 al.add("abc2"); 12 al.add(1); 13 al.add(2); 14 ListIterator<String> li=al.listIterator(); 15 while(li.hasNext()) 16 { 17 String str=(String)li.next(); 18 System.out.println(str); 19 } 20 } 21 22 }
这个代码相对上一个代码来说只是加入了泛型,其余均没有变化,但是在Eclipse中还没有运行就已经报错了。
这就是使用泛型的最大好处。同时,也应当注意,使用泛型之后,迭代器也要使用泛型来定义将要迭代的元素类型,一旦这样做之后,取出元素的时候就不需要做强转动作了。
1 package p04.GenerateTypeDemo.Demo01; 2 3 import java.util.ArrayList; 4 import java.util.ListIterator; 5 6 public class Demo01 { 7 public static void main(String args[]) 8 { 9 ArrayList<String>al=new ArrayList<String>(); 10 al.add("abc1"); 11 al.add("abc2"); 12 al.add("abc3"); 13 al.add("abc4"); 14 ListIterator<String> li=al.listIterator(); 15 while(li.hasNext()) 16 { 17 String str=li.next(); 18 System.out.println(str); 19 } 20 } 21 22 }
2.总结:
泛型是什么:泛型是JDK1.5之后出现的新特性。
使用泛型的目的:为了提高安全机制。(JDK升级几乎只为了三个目的:提高效率、简化书写、提高安全性)
使用泛型的好处:
1.将运行时期的问题ClasscastException转到了编译时期。
2.避免了强制转换的麻烦。
解惑:<>和E
<>是什么?
就像方法中使用()来界定参数范围,泛型使用<>界定要传入的参数类型。
<>什么时候用?
当操作的引用数据类型不确定的时候使用。
E是什么?
E代表一个参数,为Element的简写,不使用小写的原因就是E代表的参数类型只限于引用型数据类型,而不包括基本数据类型。
3.泛型的擦除和补偿。
擦除:虽然程序员在写代码的时候使用了泛型,但是在JAVA编译器生成Class文件的时候,会将泛型去掉,生成的Class文件中并没有泛型。这称为泛型的擦除。
补偿:擦除的目的是为了兼容低版本jre,但是泛型技术中不使用强制转换却没有办法使得低版本支持,所以编译器略作调整,它将自动获取对象类型(使用getClass方法)并完成隐式的强转动作。这就是泛型的补偿。
4.泛型类型所能使用的方法。
一旦使用了泛型,则变量类型变得不确定,它将不能使用某个类的具体方法,但是能够使用Object类的所有方法。
二、泛型类、泛型方法、泛型接口。
1.泛型在TreeSet集合中的使用。
TreeSet在集合框架中是比较复杂的一个容器,所以使用它作为演示容器。
泛型在TreeSet中的常用使用方法:
按照年龄排序:
1 package p04.GenerateTypeDemo.Demo02.TreeSetDemo; 2 3 import java.util.Iterator; 4 import java.util.TreeSet; 5 6 class Person implements Comparable<Person> 7 { 8 private String name; 9 private int age; 10 11 public Person() { 12 super(); 13 } 14 public Person(String name, int age) { 15 super(); 16 this.name = name; 17 this.age = age; 18 } 19 @Override 20 public int hashCode() { 21 final int prime = 31; 22 int result = 1; 23 result = prime * result + age; 24 result = prime * result + ((name == null) ? 0 : name.hashCode()); 25 return result; 26 } 27 @Override 28 public boolean equals(Object obj) { 29 if (this == obj) 30 return true; 31 if (obj == null) 32 return false; 33 if (getClass() != obj.getClass()) 34 return false; 35 Person other = (Person) obj; 36 if (age != other.age) 37 return false; 38 if (name == null) { 39 if (other.name != null) 40 return false; 41 } else if (!name.equals(other.name)) 42 return false; 43 return true; 44 } 45 @Override 46 public String toString() { 47 return "Person [name=" + name + ", age=" + age + "]\n"; 48 } 49 public String getName() { 50 return name; 51 } 52 public void setName(String name) { 53 this.name = name; 54 } 55 public int getAge() { 56 return age; 57 } 58 public void setAge(int age) { 59 this.age = age; 60 } 61 @Override 62 public int compareTo(Person o) { 63 //建立排序规则,按照年龄进行整体排序,如果年龄相同,则按照姓名的字典序排序。 64 int temp=this.age-o.getAge(); 65 return temp==0?this.name.compareTo(o.getName()):temp; 66 } 67 } 68 public class TreeSetDemo { 69 70 public static void main(String[] args) { 71 TreeSet<Person> ts=new TreeSet<Person>(); 72 ts.add(new Person("zhangsan",25)); 73 ts.add(new Person("lisib",24)); 74 ts.add(new Person("lisia",24)); 75 ts.add(new Person("wangwu",29)); 76 ts.add(new Person("zhaoliu",22)); 77 78 for(Iterator<Person>it=ts.iterator();it.hasNext();) 79 { 80 Person p=it.next(); 81 System .out.println(p); 82 } 83 84 } 85 86 }
覆盖自然排序,使用比较器按照姓名字典序排序:
1 package p04.GenerateTypeDemo.Demo02.TreeSetDemo02; 2 3 import java.util.Comparator; 4 import java.util.Iterator; 5 import java.util.TreeSet; 6 7 class Person implements Comparable<Person> 8 { 9 private String name; 10 private int age; 11 12 public Person() { 13 super(); 14 } 15 public Person(String name, int age) { 16 super(); 17 this.name = name; 18 this.age = age; 19 } 20 @Override 21 public int hashCode() { 22 final int prime = 31; 23 int result = 1; 24 result = prime * result + age; 25 result = prime * result + ((name == null) ? 0 : name.hashCode()); 26 return result; 27 } 28 @Override 29 public boolean equals(Object obj) { 30 if (this == obj) 31 return true; 32 if (obj == null) 33 return false; 34 if (getClass() != obj.getClass()) 35 return false; 36 Person other = (Person) obj; 37 if (age != other.age) 38 return false; 39 if (name == null) { 40 if (other.name != null) 41 return false; 42 } else if (!name.equals(other.name)) 43 return false; 44 return true; 45 } 46 @Override 47 public String toString() { 48 return "Person [name=" + name + ", age=" + age + "]\n"; 49 } 50 public String getName() { 51 return name; 52 } 53 public void setName(String name) { 54 this.name = name; 55 } 56 public int getAge() { 57 return age; 58 } 59 public void setAge(int age) { 60 this.age = age; 61 } 62 @Override 63 public int compareTo(Person o) { 64 //建立排序规则,按照年龄进行整体排序,如果年龄相同,则按照姓名的字典序排序。 65 int temp=this.age-o.getAge(); 66 return temp==0?this.name.compareTo(o.getName()):temp; 67 } 68 } 69 public class TreeSetDemo { 70 71 public static void main(String[] args) { 72 TreeSet<Person> ts=new TreeSet<Person>(new ComparatorByName()); 73 ts.add(new Person("zhangsan",25)); 74 ts.add(new Person("lisib",24)); 75 ts.add(new Person("lisia",24)); 76 ts.add(new Person("wangwu",29)); 77 ts.add(new Person("zhaoliu",22)); 78 79 out(ts); 80 } 81 82 private static void out(TreeSet<Person> ts) { 83 for(Iterator<Person>it=ts.iterator();it.hasNext();) 84 { 85 Person p=it.next(); 86 System .out.println(p); 87 } 88 } 89 90 } 91 class ComparatorByName implements Comparator<Person> 92 { 93 @Override 94 public int compare(Person o1, Person o2) { 95 int temp=o1.getName().compareTo(o2.getName()); 96 return temp==0?o1.getAge()-o2.getAge():temp; 97 } 98 99 }
注意点:
可以看到,不仅黄色小叹号不见了,而且在取出的时候少了强转动作。
Person类在实现Comparable接口的时候,使用的泛型不是默认的Object,而是自定义的Person,这么做的好处就是类型明确,减少出错的几率,还避免了强转。
equals方法不能修改参数类型,其参数必须是Object,想要使用必须强转。
使用比较器的时候可以指定接受的泛型类型,这里是Person。
自定义的比较器ComparatorByName只重写了compare方法,没有重写equals方法,原因是因为继承了Object类,所以已经默认被重写。
2.泛型类。
如果不使用泛型,取出对象的时候会产生异常:ClassCastException。
1 package p04.GenerateTypeDemo.Demo03.GenerateClassDemo01; 2 /* 3 * 出现的问题。取出对象的时候出现了ClassCastException异常。 4 */ 5 class Person 6 { 7 private String name; 8 private String age; 9 public Person() { 10 super(); 11 } 12 public Person(String name, String age) { 13 super(); 14 this.name = name; 15 this.age = age; 16 } 17 public String getName() { 18 return name; 19 } 20 public void setName(String name) { 21 this.name = name; 22 } 23 public String getAge() { 24 return age; 25 } 26 public void setAge(String age) { 27 this.age = age; 28 } 29 @Override 30 public String toString() { 31 return "Person [name=" + name + ", age=" + age + "]\n"; 32 } 33 34 } 35 class Student extends Person 36 { 37 public Student() { 38 super(); 39 } 40 public Student(String name, String age) { 41 super(name, age); 42 } 43 @Override 44 public String toString() { 45 return "Student [toString()=" + super.toString() + "]\n"; 46 } 47 48 } 49 class Worker extends Person 50 { 51 public Worker() { 52 super(); 53 } 54 public Worker(String name, String age) { 55 super(name, age); 56 } 57 @Override 58 public String toString() { 59 return "Worker [toString()=" + super.toString() + "]\n"; 60 } 61 62 } 63 class Tool 64 { 65 public Object p; 66 public Tool(Object p) { 67 super(); 68 this.p = p; 69 } 70 public Tool() { 71 super(); 72 } 73 public Object getP() { 74 return p; 75 } 76 public void setP(Object p) { 77 this.p = p; 78 } 79 } 80 public class GenerateClassDemo { 81 public static void main(String args[]) 82 { 83 Tool tool=new Tool(); 84 tool.setP(new Student("张三","23")); 85 Student stu=(Student)tool.getP(); 86 System.out.println(stu); 87 88 tool.setP(new Worker("李四","24")); 89 stu=(Student)tool.getP(); 90 System.out.println(stu); 91 } 92 }
分析:装错了对象,即不应当装Worker类的对象,这在编译时期就应当注意到。
在JDK1.5之后,使用泛型来接受类中要操作的引用数据类型。
泛型类:当类中操作的引用数据类型不确定的时候就使用泛型来表示。
解决方法:改进Tool类,使用泛型类 class Tool<T>
1 package p04.GenerateTypeDemo.Demo03.GenerateClassDemo01; 2 /* 3 * 出现的问题。取出对象的时候出现了ClassCastException异常。 4 */ 5 class Person 6 { 7 private String name; 8 private String age; 9 public Person() { 10 super(); 11 } 12 public Person(String name, String age) { 13 super(); 14 this.name = name; 15 this.age = age; 16 } 17 public String getName() { 18 return name; 19 } 20 public void setName(String name) { 21 this.name = name; 22 } 23 public String getAge() { 24 return age; 25 } 26 public void setAge(String age) { 27 this.age = age; 28 } 29 @Override 30 public String toString() { 31 return "Person [name=" + name + ", age=" + age + "]\n"; 32 } 33 34 } 35 class Student extends Person 36 { 37 public Student() { 38 super(); 39 } 40 public Student(String name, String age) { 41 super(name, age); 42 } 43 @Override 44 public String toString() { 45 return "Student [toString()=" + super.toString() + "]\n"; 46 } 47 48 } 49 class Worker extends Person 50 { 51 public Worker() { 52 super(); 53 } 54 public Worker(String name, String age) { 55 super(name, age); 56 } 57 @Override 58 public String toString() { 59 return "Worker [toString()=" + super.toString() + "]\n"; 60 } 61 62 } 63 class Tool<T> 64 { 65 public T p; 66 public Tool(T p) { 67 super(); 68 this.p = p; 69 } 70 public Tool() { 71 super(); 72 } 73 public T getP() { 74 return p; 75 } 76 public void setP(T p) { 77 this.p = p; 78 } 79 } 80 public class GenerateClassDemo { 81 public static void main(String args[]) 82 { 83 Tool<Student> tool=new Tool<Student>(); 84 tool.setP(new Student("张三","23")); 85 Student stu=(Student)tool.getP(); 86 System.out.println(stu); 87 88 tool.setP(new Worker("李四","24")); 89 stu=(Student)tool.getP(); 90 System.out.println(stu); 91 } 92 }
这段代码不会编译成功,Eclipse会报错,这样就将运行时的问题转移到了编译时期。
但是应当注意,如果在创建Tool对象的时候使用了Person类作为泛型类型,即使你只想要装Student对象,但是如果你一不小心装上了Worker对象,Eclipse也不会报错,原因很明显,不赘述,但是应当注意,这时候发生的错误就不是“失误”了,而是纯碎的逻辑错误了。
使用Person类,Eclipse不会报错,但是在运行的时候会抛出classCastException异常。
1 package p04.GenerateTypeDemo.Demo03.GenerateClassDemo01; 2 /* 3 * 出现的问题。取出对象的时候出现了ClassCastException异常。 4 */ 5 class Person 6 { 7 private String name; 8 private String age; 9 public Person() { 10 super(); 11 } 12 public Person(String name, String age) { 13 super(); 14 this.name = name; 15 this.age = age; 16 } 17 public String getName() { 18 return name; 19 } 20 public void setName(String name) { 21 this.name = name; 22 } 23 public String getAge() { 24 return age; 25 } 26 public void setAge(String age) { 27 this.age = age; 28 } 29 @Override 30 public String toString() { 31 return "Person [name=" + name + ", age=" + age + "]\n"; 32 } 33 34 } 35 class Student extends Person 36 { 37 public Student() { 38 super(); 39 } 40 public Student(String name, String age) { 41 super(name, age); 42 } 43 @Override 44 public String toString() { 45 return "Student [toString()=" + super.toString() + "]\n"; 46 } 47 48 } 49 class Worker extends Person 50 { 51 public Worker() { 52 super(); 53 } 54 public Worker(String name, String age) { 55 super(name, age); 56 } 57 @Override 58 public String toString() { 59 return "Worker [toString()=" + super.toString() + "]\n"; 60 } 61 62 } 63 class Tool<T> 64 { 65 public T p; 66 public Tool(T p) { 67 super(); 68 this.p = p; 69 } 70 public Tool() { 71 super(); 72 } 73 public T getP() { 74 return p; 75 } 76 public void setP(T p) { 77 this.p = p; 78 } 79 } 80 public class GenerateClassDemo { 81 public static void main(String args[]) 82 { 83 Tool<Student> tool=new Tool<Student>(); 84 tool.setP(new Student("张三","23")); 85 Student stu=(Student)tool.getP(); 86 System.out.println(stu); 87 88 tool.setP(new Worker("李四","24")); 89 stu=(Student)tool.getP(); 90 System.out.println(stu); 91 } 92 }
3.泛型方法。
在方法上定义泛型应当将泛型放在返回值之前,修饰符之后。
前者是定义泛型,后者是使用泛型。
泛型方法分为非静态泛型方法和静态泛型方法。
改造Tool类,使得Tool类能够使用show方法show出任意类型。分别使用非静态方法和静态方法的泛型表示形式。需要注意区别的是,静态方法的泛型只能加在方法上,即静态泛型方法不能访问类上定义的泛型,原因很明显,略。
1 package p04.GenerateTypeDemo.Demo04.GenerateFunctionDemo01; 2 3 /* 4 * 泛型方法示例。 5 */ 6 class Person 7 { 8 private String name; 9 private String age; 10 public Person() { 11 super(); 12 } 13 public Person(String name, String age) { 14 super(); 15 this.name = name; 16 this.age = age; 17 } 18 public String getName() { 19 return name; 20 } 21 public void setName(String name) { 22 this.name = name; 23 } 24 public String getAge() { 25 return age; 26 } 27 public void setAge(String age) { 28 this.age = age; 29 } 30 @Override 31 public String toString() { 32 return "Person [name=" + name + ", age=" + age + "]\n"; 33 } 34 35 } 36 class Student extends Person 37 { 38 public Student() { 39 super(); 40 } 41 public Student(String name, String age) { 42 super(name, age); 43 } 44 @Override 45 public String toString() { 46 return "Student [toString()=" + super.toString() + "]\n"; 47 } 48 49 } 50 class Worker extends Person 51 { 52 public Worker() { 53 super(); 54 } 55 public Worker(String name, String age) { 56 super(name, age); 57 } 58 @Override 59 public String toString() { 60 return "Worker [toString()=" + super.toString() + "]\n"; 61 } 62 63 } 64 class Tool<T> 65 { 66 public T p; 67 public Tool(T p) { 68 super(); 69 this.p = p; 70 } 71 public Tool() { 72 super(); 73 } 74 public <W>void show(W w)//如果不是泛型方法,则应当注意泛型只能使用类提供的泛型 75 { 76 System.out.println(w); 77 } 78 public static<Q>void show_1(Q q)//静态方法的泛型必须加在方法上 79 { 80 System.out.println(q); 81 } 82 public T getP() { 83 return p; 84 } 85 public void setP(T p) { 86 this.p = p; 87 } 88 } 89 public class GnerateFunctionDemo { 90 91 public static void main(String args[]) 92 { 93 Tool <Student>tool=new Tool<Student>(); 94 Student stu=new Student("zhangsan","23"); 95 tool.show(stu); 96 97 tool.show(new Worker("lisi","24")); 98 tool.show(new Worker("wangwu","25")); 99 } 100 101 102 }
4.泛型接口。
泛型接口的定义很简单,和泛型类的定义几乎相同。
实现泛型接口的类可以明确要使用的类型,也可以不明确要使用的类型,如果实现泛型接口的时候还不明确要使用的类型,则此类将是泛型类。
下面分别演示两种实现类。
1 package p04.GenerateTypeDemo.Demo05.GenerateInterfaceDemo01; 2 3 /* 4 * 泛型接口。 5 */ 6 interface Inter<T> 7 { 8 public void show(T t); 9 } 10 class InterImpl implements Inter<String>//确定了类型的实现类 11 { 12 @Override 13 public void show(String t) { 14 System.out.println(t); 15 } 16 } 17 class InterImpll <Q>implements Inter<Q>//不确定类型的实现类。 18 { 19 @Override 20 public void show(Q t) { 21 System.out.println(t); 22 } 23 } 24 public class GenerateInterfaceDemo01 { 25 public static void main(String args[]) 26 { 27 InterImpl ii=new InterImpl(); 28 ii.show(new String("zhangsan")); 29 30 InterImpll<String> iil=new InterImpll<String>(); 31 iil.show(new Integer("123").toString()); 32 33 } 34 }
三、泛型的上限和下限
1.泛型的通配符?
?是泛型的通配符,作用和E相似,都代表了不确定的类型。
可以使用以下代码遍历容器:单独的一个?代表着? extends Object
1 public static void Traversal03(Collection<?>coll) 2 { 3 for(Iterator<?>it=coll.iterator();it.hasNext();) 4 { 5 System.out.println(it.next()); 6 } 7 }
也可以使用泛型方法遍历容器:
1 public static<E> void Traversal04(Collection<E>coll) 2 { 3 for(Iterator<E>it=coll.iterator();it.hasNext();) 4 { 5 System.out.println(it.next()); 6 } 7 }
以上两个代码作用是完全相同的,但是使用泛型方法更有优势:可以将对象取出来并加以其它操作,所以使用泛型方法的情况比较多。
1 public static<E> void Traversal05(Collection<E>coll) 2 { 3 for(Iterator<E>it=coll.iterator();;) 4 { 5 E e=it.next(); 6 System.out.println(e); 7 } 8 }
2.泛型的上限
现在有两个ArrayList容器分别存储Person类对象和Student类对象,如果想要迭代两个容器,该怎么做?如果迭代的方法参数中接收的类型是Collection<Person>则会在
print(al1);
报错。
1 package p04.GenerateTypeDemo.Demo06.GenerateUpperLimitDemo02; 2 3 import java.util.ArrayList; 4 import java.util.Collection; 5 6 class Person 7 { 8 private String name; 9 private int age; 10 11 public Person() { 12 super(); 13 } 14 public Person(String name, int age) { 15 super(); 16 this.name = name; 17 this.age = age; 18 } 19 public String getName() { 20 return name; 21 } 22 public void setName(String name) { 23 this.name = name; 24 } 25 public int getAge() { 26 return age; 27 } 28 public void setAge(int age) { 29 this.age = age; 30 } 31 32 } 33 class Student extends Person 34 { 35 36 public Student() { 37 super(); 38 } 39 40 public Student(String name, int age) { 41 super(name, age); 42 } 43 44 } 45 class Worker extends Person 46 { 47 48 public Worker() { 49 super(); 50 } 51 52 public Worker(String name, int age) { 53 super(name, age); 54 } 55 56 } 57 public class GenerateUpperLimitDemo { 58 public static void main(String args[]) 59 { 60 ArrayList <Person>al=new ArrayList<Person>(); 61 al.add(new Person("zhangsan",23)); 62 al.add(new Person("lisi",24)); 63 print(al); 64 65 ArrayList<Student>al1=new ArrayList<Student>(); 66 al1.add(new Student("wangwu",25)); 67 al1.add(new Student("zhaoliu",26)); 68 print(al1); 69 } 70 71 private static void print(Collection<Person> al) 72 { 73 74 } 75 76 }
报错的原因是泛型类型不匹配,相当于代码Collection<Person>col=new ArrayList<Student>();
解决方法:使用泛型的上限。
使用方法:? extends XXX,代表着可以接受XXX类的对象和XXX类的子类对象。
1 package p04.GenerateTypeDemo.Demo06.GenerateUpperLimitDemo02; 2 3 import java.util.ArrayList; 4 import java.util.Collection; 5 import java.util.Iterator; 6 7 class Person 8 { 9 private String name; 10 private int age; 11 12 public Person() { 13 super(); 14 } 15 public Person(String name, int age) { 16 super(); 17 this.name = name; 18 this.age = age; 19 } 20 public String getName() { 21 return name; 22 } 23 public void setName(String name) { 24 this.name = name; 25 } 26 public int getAge() { 27 return age; 28 } 29 public void setAge(int age) { 30 this.age = age; 31 } 32 33 } 34 class Student extends Person 35 { 36 37 public Student() { 38 super(); 39 } 40 41 public Student(String name, int age) { 42 super(name, age); 43 } 44 45 } 46 class Worker extends Person 47 { 48 49 public Worker() { 50 super(); 51 } 52 53 public Worker(String name, int age) { 54 super(name, age); 55 } 56 57 } 58 public class GenerateUpperLimitDemo { 59 public static void main(String args[]) 60 { 61 ArrayList <Person>al=new ArrayList<Person>(); 62 al.add(new Person("zhangsan",23)); 63 al.add(new Person("lisi",24)); 64 print(al); 65 66 ArrayList<Student>al1=new ArrayList<Student>(); 67 al1.add(new Student("wangwu",25)); 68 al1.add(new Student("zhaoliu",26)); 69 print(al1); 70 } 71 72 private static void print(Collection<? extends Person> al) 73 { 74 for(Iterator<? extends Person>it=al.iterator();it.hasNext();) 75 { 76 Person p=it.next(); 77 System.out.println(p.getName()+":"+p.getAge()); 78 } 79 } 80 81 }
其中,这段代码是重点:
1 private static void print(Collection<? extends Person> al) 2 { 3 for(Iterator<? extends Person>it=al.iterator();it.hasNext();) 4 { 5 Person p=it.next(); 6 System.out.println(p.getName()+":"+p.getAge()); 7 } 8 }
这段代码可以使用泛型方法或者一个泛型通配符来解决,但是这样做并不如使用泛型的上限效果好。
使用上限有什么好处?
(1)可以明确一个具体的父类,这样就可以拿到父类中的所有方法。
(2)可以限定可以接受的参数范围,而不是所有的类型(相对于普通的泛型方法)
3.泛型的下限。
用法:? super XXX;表示可以接受的参数范围包括XXX类以及XXX类的父类。
举例:
1 package p04.GenerateTypeDemo.Demo07.GenerateDownLimitDemo01; 2 3 import java.util.Comparator; 4 import java.util.TreeSet; 5 6 class Person implements Comparable<Person> 7 { 8 private String name; 9 private int age; 10 11 @Override 12 public String toString() { 13 return "Person [name=" + name + ", age=" + age + "]\n"; 14 } 15 public Person() { 16 super(); 17 } 18 public Person(String name, int age) { 19 super(); 20 this.name = name; 21 this.age = age; 22 } 23 public String getName() { 24 return name; 25 } 26 public void setName(String name) { 27 this.name = name; 28 } 29 public int getAge() { 30 return age; 31 } 32 public void setAge(int age) { 33 this.age = age; 34 } 35 @Override 36 public int compareTo(Person o) { 37 int temp=this.age-o.getAge(); 38 return temp==0?this.name.compareTo(o.getName()):temp; 39 } 40 } 41 class Student extends Person 42 { 43 44 public Student() { 45 super(); 46 } 47 48 public Student(String name, int age) { 49 super(name, age); 50 } 51 52 } 53 class Worker extends Person 54 { 55 56 public Worker() { 57 super(); 58 } 59 60 public Worker(String name, int age) { 61 super(name, age); 62 } 63 64 } 65 public class GnerateDownLimitDemo { 66 public static void main(String args[]) 67 { 68 //Demo1(); 69 Demo2(); 70 } 71 72 /* 73 * 该方法演示传入父类比较器仍然能够正常添加元素 74 */ 75 private static void Demo2() { 76 TreeSet <Student>ts=new TreeSet<Student>(new ComparatorByAny()); 77 ts.add(new Student("zhangsan",23)); 78 ts.add(new Student("lisi",24)); 79 System.out.println(ts); 80 81 TreeSet<Worker>tst=new TreeSet<Worker>(new ComparatorByAny()); 82 tst.add(new Worker("zhaoliu",26)); 83 tst.add(new Worker("wangwu",25)); 84 System.out.println(tst); 85 86 } 87 88 private static void Demo1() { 89 TreeSet <Student>ts=new TreeSet<Student>(new ComparatorByStudent()); 90 ts.add(new Student("zhangsan",23)); 91 ts.add(new Student("lisi",24)); 92 System.out.println(ts); 93 94 TreeSet<Worker>tst=new TreeSet<Worker>(new ComparatorByWorker()); 95 tst.add(new Worker("zhaoliu",26)); 96 tst.add(new Worker("wangwu",25)); 97 System.out.println(tst); 98 } 99 } 100 class ComparatorByStudent implements Comparator<Student> 101 { 102 103 @Override 104 public int compare(Student o1, Student o2) { 105 int temp=o1.getName().compareTo(o2.getName()); 106 return temp==0?o1.getAge()-o2.getAge():temp; 107 } 108 109 } 110 class ComparatorByWorker implements Comparator<Worker> 111 { 112 @Override 113 public int compare(Worker o1, Worker o2) { 114 int temp=o1.getName().compareTo(o2.getName()); 115 return temp==0?o1.getAge()-o2.getAge():temp; 116 } 117 } 118 class ComparatorByAny implements Comparator<Person> 119 { 120 @Override 121 public int compare(Person o1, Person o2) { 122 int temp=o1.getName().compareTo(o2.getName()); 123 return temp==0?o1.getAge()-o2.getAge():temp; 124 } 125 126 }
4.泛型上限的体现。
Collection类的addAll方法:
boolean |
addAll(Collection<? extends E> c) 将指定 collection 中的所有元素都添加到此 collection 中(可选操作)。 |
为什么在这里要使用泛型的上限?
为了增强扩展性。上限一般在存储元素的时候使用,表示无论是E类型还是E的子类型,都可以存入容器,而取出的时候统一使用E类型取出,这样不会出现类型安全隐患。反之,如果不加限定,取出的时候就很困难了。事实上这里使用一个E就可以了,但是考虑到扩展性,使用了泛型的上限。
上限的使用比较多一些(相对于下限)。
5.泛型下限的体现。
TreeSet的一个构造方法使用了泛型的下限:
TreeSet(Comparator<? super E> comparator) 构造一个新的空 TreeSet,它根据指定比较器进行排序。 |
实例:
1 package p04.GenerateTypeDemo.Demo07.GenerateDownLimitDemo01; 2 3 import java.util.ArrayList; 4 import java.util.Collection; 5 import java.util.Comparator; 6 import java.util.Iterator; 7 import java.util.TreeSet; 8 9 class Person implements Comparable<Person> 10 { 11 private String name; 12 private int age; 13 14 @Override 15 public String toString() { 16 return "Person [name=" + name + ", age=" + age + "]\n"; 17 } 18 public Person() { 19 super(); 20 } 21 public Person(String name, int age) { 22 super(); 23 this.name = name; 24 this.age = age; 25 } 26 public String getName() { 27 return name; 28 } 29 public void setName(String name) { 30 this.name = name; 31 } 32 public int getAge() { 33 return age; 34 } 35 public void setAge(int age) { 36 this.age = age; 37 } 38 @Override 39 public int compareTo(Person o) { 40 int temp=this.age-o.getAge(); 41 return temp==0?this.name.compareTo(o.getName()):temp; 42 } 43 } 44 class Student extends Person 45 { 46 47 public Student() { 48 super(); 49 } 50 51 public Student(String name, int age) { 52 super(name, age); 53 } 54 55 } 56 class Worker extends Person 57 { 58 59 public Worker() { 60 super(); 61 } 62 63 public Worker(String name, int age) { 64 super(name, age); 65 } 66 67 } 68 public class GnerateDownLimitDemo { 69 public static void main(String args[]) 70 { 71 //Demo1(); 72 Demo2(); 73 } 74 /* 75 * 该方法演示传入 76 */ 77 private static void Demo2() { 78 TreeSet <Student>ts=new TreeSet<Student>(new ComparatorByAny()); 79 ts.add(new Student("zhangsan",23)); 80 ts.add(new Student("lisi",24)); 81 System.out.println(ts); 82 83 TreeSet<Worker>tst=new TreeSet<Worker>(new ComparatorByAny()); 84 tst.add(new Worker("zhaoliu",26)); 85 tst.add(new Worker("wangwu",25)); 86 System.out.println(tst); 87 88 } 89 90 private static void Demo1() { 91 TreeSet <Student>ts=new TreeSet<Student>(new ComparatorByStudent()); 92 ts.add(new Student("zhangsan",23)); 93 ts.add(new Student("lisi",24)); 94 System.out.println(ts); 95 96 TreeSet<Worker>tst=new TreeSet<Worker>(new ComparatorByWorker()); 97 tst.add(new Worker("zhaoliu",26)); 98 tst.add(new Worker("wangwu",25)); 99 System.out.println(tst); 100 } 101 } 102 class ComparatorByStudent implements Comparator<Student> 103 { 104 105 @Override 106 public int compare(Student o1, Student o2) { 107 int temp=o1.getName().compareTo(o2.getName()); 108 return temp==0?o1.getAge()-o2.getAge():temp; 109 } 110 111 } 112 class ComparatorByWorker implements Comparator<Worker> 113 { 114 @Override 115 public int compare(Worker o1, Worker o2) { 116 int temp=o1.getName().compareTo(o2.getName()); 117 return temp==0?o1.getAge()-o2.getAge():temp; 118 } 119 } 120 class ComparatorByAny implements Comparator<Person> 121 { 122 @Override 123 public int compare(Person o1, Person o2) { 124 int temp=o1.getName().compareTo(o2.getName()); 125 return temp==0?o1.getAge()-o2.getAge():temp; 126 } 127 128 }
在这个例子中,比较器只接受Person类型的,但是装有Student对象的TreeSet容器以及装有Worker对象的TreeSet容器都可以使用这个比较器。这是因为TreeSet的构造方法中明确了比较器中可以接受的参数范围包括E类型以及E的父类型。所以,当插入Student对象的时候,虽然使用了Person的比较器,但是由于Person是Student的父类,满足? super Student条件,所以可以进行比较并成功插入;Worker同理。
使用了泛型的下限,则只能接受E以及E的父类型。
6.通配符的体现。
List接口中的方法:
boolean |
removeAll(Collection<?> c) 从列表中移除指定 collection 中包含的其所有元素(可选操作)。 |
boolean |
retainAll(Collection<?> c)
仅在列表中保留指定 collection 中所包含的元素(可选操作)。 |
为什么要使用'?'?
我们要知道List接口中的removeAll方法和retainAll方法底层使用的都是equals方法,使用的参数是Object类型的,所以可以传入各种类型的参数,所以实际参数类型不确定,所以使用?。