学习目标
1 泛型的概念
1.1 泛型的概述
举例:
java.lang.Comparable接口和java.util.Comparator接口,是用于对象比较大小的规范接口,这两个接口只是限定了当一个对象大于另一个对象时返回正整数,小于返回负整数,等于返回0。但是并不确定是什么类型的对象比较大小,之前的时候只能用Object类型表示,使用时既麻烦又不安全,因此JDK1.5就给它们增加了泛型。
1 2 3
| public interface Comparable<T>{ int compareTo(T o) ; }
|
1 2 3
| public interface Comparator<T>{ int compare(T o1, T o2) ; }
|
其中就是类型参数,即泛型。
1.2 泛型的好处
引入泛型的好处
既能保证安全,又能简化代码。
因为把不安全的因素在编译期间就排除了;既然通过了编译,那么类型一定是符合要求的,就避免了类型转换
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| package com.buxianxian;
import java.util.ArrayList; import java.util.Collection; import java.util.Iterator;
public class Demo{
public static void main(String[] args) {
Collection c = new ArrayList(); c.add(new Student("张三",23)); c.add(new Student("李四",24)); c.add(new Student("王五",25)); c.add("aab"); c.add(33); c.add(true); Iterator it = c.iterator(); while (it.hasNext()) { Object obj = it.next();
Student s = (Student)obj;
}
} }
|
Student类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| package com.buxianxian;
public class Student {
private String name; private int age;
public Student() { }
public Student(String name, int age) { 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 String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
|
2 泛型的应用

1 泛型类
声明类或接口时,在类名或接口名后面声明类型变量,我们把这样的类或接口称为泛型类或泛型接口
例如:
1
| public class ArrayList<E>{}
|
泛型类的类型在创建对象时被确认
1、使用核心类库中的泛型类
自从JDK1.5引入泛型的概念之后,对之前核心类库中的API做了很大的修改,例如:集合框架集中的相关接口和类、java.lang.Comparable接口、java.util.Comparator接口、Class类等等。
下面以ArrayList集合为例演示泛型的使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.buxianxian.stringdemo;
import java.util.ArrayList;
public class Demo {
public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("a"); list.add("b"); list.add("c"); list.add("d"); list.add("e"); list.add("f");
System.out.println(list); }
}
|

2、自定义泛型类
格式: 修饰符 class 类名<类型>{}
<类型>: 指定一种类型的格式,里面可以任意书写,按变量定义规则即可,一般只写一个字母
1 2
| 示例:public class Box<T>{ }
|
此处的T可以随意写任意的大写标识,常见的有T、E、K、v等标识泛型
代码演示
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.buxianxian.stringdemo;
public class Box<E> { private E element;
public E getElement() { return element; }
public void setElement(E element) { this.element = element; } }
|
测试泛型类的应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.buxianxian.stringdemo;
public class Demo {
public static void main(String[] args) {
Box<String> box1 = new Box<>(); box1.setElement("革命尚未成功,同志还需努力"); System.out.println(box1.getElement());
Box<Integer> box2 = new Box<>(); box2.setElement(20); System.out.println(box2.getElement()); }
}
|
泛型对应的类型均按照Object处理,但不等价于Object
2 泛型接口
1、核心类中的泛型接口
java.util.List
实现类 java.util.ArrayList
泛型接口的使用方式
1> 实现类没有确定具体的数据类型(ArrayList属于此类)
2> 实现类确定了具体的数据类型

2、自定义泛型接口
在创建对象时,确认泛型的数据类型
具体的实现类,不需要写泛型
1 2
| 格式: 修饰符 interface 接口名<类型> { } 示例: public interface Book<T>{}
|
代码演示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| package com.buxianxian.stringdemo;
import java.util.ArrayList;
public class Demo {
public static void main(String[] args) {
new BookImpl1<String>().method("书中自有黄金屋"); new BookImpl1<Boolean>().method(true);
new BookImpl2().method(333);
} }
interface Book<E>{ void method(E e); }
class BookImpl1<E> implements Book<E>{
@Override public void method(E e) { System.out.println(e); } }
class BookImpl2 implements Book<Integer>{
@Override public void method(Integer integer) { System.out.println(integer); } }
|
3 泛型方法
1、使用核心类中的泛型方法
在java.util.ArrayList中有个泛型方法
T[] toArray(T[] a) 按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。
Object[] toArray() 按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组。
代码演示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package com.buxianxian.stringdemo;
import java.util.ArrayList; import java.util.Arrays;
public class Demo {
public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("a"); list.add("b"); list.add("c"); list.add("d"); list.add("e"); list.add("f"); System.out.println(list);
Object[] objects = list.toArray(); System.out.println(Arrays.toString(objects));
String[] strings = list.toArray(new String[list.size()]); System.out.println(Arrays.toString(strings)); }
}
|
2、自定义泛型方法
方法在调用时才能确认泛型类型,声明方法时,在【修饰符】与返回值类型之间声明类型变量,我们把声明了类型变量的方法称为泛型方法
1 2 3
| 格式:修饰符<类型> 返回值类型 方法名(类型 变量名){}
示例:public <T> void show(T t){}
|
代码演示
定义一个泛型方法,传递一个集合和四个元素,将元素添加到集合并返回
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package com.buxianxian.stringdemo;
import java.util.ArrayList;
public class Demo {
public static void main(String[] args) {
ArrayList<String> list1 = addElement(new ArrayList<String>(), "aa", "bb", "cc", "dd"); System.out.println(list1);
}
public static <T> ArrayList<T> addElement(ArrayList<T> list,T t1, T t2,T t3, T t4) { list.add(t1); list.add(t2); list.add(t3); list.add(t4); return list; } }
|
3、知识点扩展
1 泛型方法的补充
一个类中可以出现多个泛型
如果一个方法 ,声明上没有泛型,但是使用到了泛型,默认认为方法的泛型和类的泛型是一致的(推荐方法的泛型与类的泛型一致)
如果 一个实例方法的泛型和当前类的泛型是一致的,那么 <泛型类型> 是可以省略的( 修饰符 <泛型类型> 返回值类型)
如果一个方法,使用泛型时,类型和类的泛型不一致 ,那么必须要在方法声明上标出
如果一个静态方法,使用泛型,必须要单独定义,而且要和类泛型不一致(即使是相同的字母,代表的也是不同的类型)
1 2 3 4 5 6 7
| class GenericClass<T> { static <U> void print(U element) { System.out.println(element); } }
|
4.静态方法的加载时机是随着类的加载而加载,优先于对象,所以必须单独定义(在静态方法中不能使用类的泛型。)
| | 数据类型确定时间 |
| ———- | —————- | —- |
| 类的泛型 | 创建对象时 |
| 方法的泛型 | 调用方法时 |
2 泛型数组
不能使用new E[]。但是可以:E[] elements = (E[]) new Object[capacity];
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class Order<T> { String orderName; int orderId; T orderT; public Order(){ T[] arr = new T[10]; T[] arr = (T[]) new Object[10]; }
|
参考: ArrayList源码中声明: Object elementData,而非泛型参数类型数组。
3 子类继承父类时泛型的不同情况


在这个例子中,Parent类是一个泛型类,Child1、Child2和Child3是它的子类。
Child1类是按需实现泛型,它继承自Parent<Integer>,在实例化时指定了具体的类型为Integer。
Child2类完全保留了父类的泛型,它继承自Parent<T>,在实例化时指定了具体的类型为String。
Child3类部分保留了父类的泛型,它继承自Parent<T>,并添加了一个额外的泛型类型U,在实例化时指定了T为Integer,U为String。
4 类型通配符
1.类型通配符
代码演示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| package com.buxianxian.stringdemo;
import java.util.ArrayList;
public class Demo {
public static void main(String[] args) {
ArrayList<Integer> list1 = new ArrayList<>(); ArrayList<String> list2 = new ArrayList<>(); ArrayList<Number> list3 = new ArrayList<>(); ArrayList<Object> list4 = new ArrayList<>();
method1(list1); method1(list2); method1(list3); method1(list4); method2(list1); method2(list2); method2(list3); method2(list4);
method3(list1); method3(list2); method3(list3); method3(list4); }
public static void method1(ArrayList<?> list){}
public static void method2(ArrayList<? extends Number> list){} public static void method3(ArrayList<? super Number> list){}
}
|
2. 通配符小结
? extends A:
G<? extends A> 可以作为G和G的子类,其中B是A的子类 ? super A:
G<? super A> 可以作为G和G的父类,其中B是A的父类