Java1.5之后出现的类型安全机制;
好处:
- 运行时期出现的ClassCastExcepton异常,转换到编译时期。方便程序员解决问题,安全!
- 避免类型强转带来的麻烦。
格式:
- 通过<>来定义要操作的类型。
什么时候使用:
- 集合框架中常用。
- 自定义泛型类,当类中要操作的数据类型不确定时,早期用Object来完成;现在定义泛型类就可以完成。
用的地方:
- 除了可以定义在类上,也可以定义在方法上。写法都差不多:在类名后面加上<T>表T类型;其中T也可以用别的字母代替。
- 方法上只不过是将T作为参数传递进去就OK。
- 用了泛型的方法,传啥都行,不用指定参数类型。
泛型的缺点:
- 如果泛型定义在类上,那么该类中的方法都得用同一种数据类型参数,这程序设计有很大的局限性。
- 于是我们可以直接把类型定义在方法上,格式:在返回值类型前面加上 <T>就可以了;原理就是把T类型的作用域减小。
注意:
- 静态方法不可以使用类上定义的泛型;如果想在静态方法上使用泛型,可以直接在静态方法上定义泛型;在返回值类型前面加上<T>就OK。这时就不用类上定义的是什么泛型!
- 我们也可以用通配符<?>来代替<T>;但这种情况有一个要求,就是类或方法里面没有出现T t=....;这样的定义。
泛型的高级应用:
- 代码如下:
public static void main(String[] args) {
ArrayList<String> al1 = new ArrayList<String>();
al1.add("abc");
al1.add("djhf");
al1.add("djhf01");
al1.add("djhf02");
ArrayList<Integer> al2 = new ArrayList<Integer>();
al2.add(3);
al2.add(4);
al2.add(5);
al2.add(6);
}
- 此时如果我们想打印这两个数组集合,我们就得定义两个迭代器。
- 这似乎做了重复工作;
- 此时,我们可以利用泛型的高级引用来解决!
- 我们可以定义一个方法,来封装这个迭代器。
public static void printIterator(ArrayList al) {
Iterator it = al.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
- 调用封装好的迭代器
printIterator(al1);
printIterator(al2);
- 打印结果:
-
问题:
printIterator方法中,并没有指定迭代器的数据类型,为什么也能打印成功?**
因为Java1.4版本之前没有泛型,Java1.5之后为了兼容之前的版本,此处参数传递时不指定数据类型也是可以的;但你这样数据类型不太安全。
还可以这样写:**
public static void printIterator(ArrayList<?> al) {
Iterator<?> it = al.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
- 结果当然一样的,其中<?>中的“?”是通配符;指任意类型!虽然数据安全问题解决了,但是这就意味着任何数据类型这个迭代器都能迭代,范围太大。
- 我们也可以这样:
public static <T> void printIterator(ArrayList<T> al) {
Iterator<T> it = al.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
- 虽然这和用通配符“?”没什么区别,但这个在方法的内部可用T数据类型来定义引用。
- 注意:如果我们想调用length()方法,来打印上面两种数据类对象的长度,用<?>或<T>或啥都不指明是无法是实现的,因为Integer类型没有该方法。这里就必须指定数据类型,这也是泛型的缺点。
泛型的限定:
要想灵活的利用好泛型,泛型限定就是最好的选择。
上限;<?> extends T
下线;<?> super T
- 现在有一个Person01类,他有一个子类Student01。现在要为他们两封装一个打印方法sopp(),该如何实现呢?
class Person01{
private String name;
private int age;
Person01(String n, int a){
this.name = n;
this.age = a;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
//Student01继承Person01类
class Student01 extends Person01{
Student01(String n, int a){
super(n, a);
}
}
- 打印方法:
public static void sopp(Person01 p) {
System.out.println(p.getName()+"..."+p.getAge());
}
}
- 定义集合:
public static void main(String[] args) {
ArrayList<Person01> al = new ArrayList<Person01>();
al.add(new Person01("lily", 12));
al.add(new Person01("Tom", 44));
al.add(new Person01("Litre", 22));
al.add(new Person01("Greson", 52));
for(int i=0; i<al.size(); i++) {
sopp(al.get(i));
}
}
-
结果:
-
此时参数类型是Person01,且集合类型也是Person01,所以就打印成功了。如果集合是Student01的话编译就会报错,因为类型不匹配。为了让Student01类型的集合也能调用这个方法,我们就要用到泛型限定的向上限定。
public static void sopp(ArrayList<? extends Person01> al) {
Iterator<? extends Person01> it = al.iterator();
while(it.hasNext()) {
System.out.println(it.next().getName());
}
}
- 调用该方法:
sopp(al);
-
结果:
- 我们定义一个Student01类型集合,看sopp()能不能打印?
ArrayList<Student01> al1 = new ArrayList<Student01>();
al1.add(new Student01("lily0001", 12));
al1.add(new Student01("Tom0001", 44));
al1.add(new Student01("Litre0001", 22));
al1.add(new Student01("Greson0001", 52));
sopp(al1);
-
结果:
- 可知Student01类型完全OK。这便是泛型的向上限定,向下先订也是一样的,就不做展示。
-
好多方法里面就有<? super E>,<? extends E>比如比较器:Comparator以及compareTo()都有用到。






网友评论