泛型
泛型即为“类型参数”,这个类型参数在声明它的类、接口或方法中,代表未知的通用的类型。
java.lang.Comparable接口和java.util.Comparator接口不确定是什么类型的对象比较大小,之前的时候只能用Object类型表示,使用时既麻烦又不安全,因此JDK1.5就给它们增加了泛型。
public interface Comparable<T>{
int compareTo(T o) ;
}
public interface Comparator<T>{
int compare(T o1, T o2) ;
}
其中就是类型参数,即泛型。
泛型的相关术语
<数据类型>这种语法形式就叫泛型。其中数据类型只能是引用数据类型。
TypeVariable
:类型变量,例如:ArrayList<E>
中的E,Map<K,V>
中的K,VParameterizedType
:参数化类型,例如:Comparator<T>
,Comparator<String>
GenericArrayType
:泛化的数组类型,即T[]
WildcardType
:通配符类型,例如:Comparator<?>
等
在哪里可以声明类型变量<T>
- 声明类或接口时,在类名或接口名后面声明类型变量,我们把这样的类或接口称为泛型类或泛型接口
【修饰符】 class 类名<类型变量列表> 【extends 父类】 【implements 父接口们】{
}
【修饰符】 interface 接口名<类型变量列表> 【implements 父接口们】{
}
例如:
public class ArrayList<E>
public interface Map<K,V>{
....
}
- 声明方法时,在【修饰符】与返回值类型之间声明类型变量,我们把声明(是声明不是单纯的使用)了类型变量的方法称为泛型方法
【修饰符】 <类型变量列表> 返回值类型 方法名(【形参列表】)【throws 异常列表】{
//...
}
例如:java.util.Arrays类中的
public static <T> List<T> asList(T... a){
....
}
-
<类型变量列表>:可以是一个或多个类型变量,一般都是使用单个的大写字母表示。例如:
<T>
、<K,V>
等。 -
当类或接口上声明了<类型变量列表>时,其中的类型变量不能用于静态成员上。
-
//public static void test(T t){ } //此时类型变量T不能用在静态成员上
-
当使用参数化类型的类或接口时,如果没有指定泛型,相当于Object类型。
案例:定义MyArrayList类
public class MyArrayList<T> {
private Object[] all = new Object[4];
private int total;
public void add(T t){
if(total >= all.length){
all = Arrays.copyOf(all, all.length*2);
}
all[total++] = t;
}
public T get(int index){
if(index<0 || index>=total){
throw new IndexOutOfBoundsException(index +"越界");
}
return (T) all[index];
}
public Object[] toArray(){
return Arrays.copyOf(all,total);
}
}
类型通配符
@Test
public void test2() {
//一、使用通配符?
List<?> list;//泛型变量可以是任意类型
list = new ArrayList<>();//泛型变量默认是Object类型的泛型
list = new ArrayList<Object>();
list = new ArrayList<String>();
list = new ArrayList<Number>();
list = new ArrayList<Integer>();
// list.add(1);//compile error 编译器无法确定要add的真实类型,可能是<Character>或<Byte>,那么add(0.5)不可以,只有在方法被调用时才能确定。
Object o = list.get(0);//无法确定元素类型,只能使用Object接收
//二、设定通配符上限
List<? extends Number> list1;//泛型变量必须是Number子类类型
list1 = new ArrayList<>();//泛型变量默认是 Number类型
// list1 = new ArrayList<Object>();//compile error
list1 = new ArrayList<Number>();
list1 = new ArrayList<Double>();
// list1.add(1);//compile error 编译器无法确定要add的真实类型,可能是<Character>或<Byte>,那么add(0.5)不可以,只有在方法被调用时才能确定。
Number number = list1.get(0);//可以使用Number接收,自动向上转型
//所以设定了通配符上限,通常只能获取数据,即生产数据
//三、设定通配符下限
List<? super Number> list2;//泛型变量必须是Number父类类型
list2 = new ArrayList<>();//泛型变量默认是 Number类型,
list2 = new ArrayList<Object>();
list2 = new ArrayList<Number>();
// list2 = new ArrayList<Integer>();//compile error
//
list2.add(1.2);//编译器可以确定要add的真实类型一定是数值类型父类,那么add一个数值类型就不会有问题。
Object object = list2.get(0);//返回值类型只能确定是Number的超类,编译器不确定具体类型,只能使用Object接收
//所以设定通配符下限,通常用于添加数据,修改数据等即消费数据
}
//1.使用通配符,可以接收任意泛型的List集合
public void handleList(List<?> list){
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(0));
}
}
//2.设定通配符上限
//定义一个方法,只能处理装有数值类型元素的List集合
public void handleNumberList(List<? extends Number> list){
double sum=0;
for (Number number : list) {
sum += number.doubleValue();
}
System.out.println(sum);
}
//3.设定通配符下限
//定一个方法,需要一个比较器,只要能处理T类型数据的比较器就可以
//比如要比较T[]的数组元素大小,那么就需要一个可以比较T类型元素或能比较T的父类型元素(一定能比较T)的比较器
public <T> void handleComparator(T[] arr,Comparator<? super T> c){
}
//3.1设定通配符下限
//定义一个方法,可以向任意List集合中(泛型类型下限为T型的集合),添加T型元素(泛型类型下限为T型的集合一定可以添加T型元素)
public static <T> void fill(List<? super T> list,T obj){
for (int i = 0; i < list.size(); i++) {
list.add(obj);
}
}
}
案例:
创建Student类
class Student implements Comparable<Student>, Comparator<Student> {
private String name;
private int age;
省略get/Set、toString以及有参无参构造器
@Override
public int compareTo(Student o) {
int i = age - o.getAge();
return i == 0 ? name.compareTo(o.getName()) : i;
}
@Override
public int compare(Student o1, Student o2) {
int i = o1.getAge() - o2.getAge();
return i == 0 ? o1.getName().compareTo(o2.getName()) : i;
}
编写工具测试类:
public class MyArrays {
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("张山", 15);
students[1] = new Student("李四", 18);
students[2] = new Student("王五", 17);
MyArrays.sort(students);
for (Student s : students) {
System.out.println(s);
}
MyArrays.sort(students, Student::compareTo);
for (Student s : students) {
System.out.println(s);
}
MyArrays.sort(students, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
int i=o1.getAge()-o2.getAge();
return i==0?o1.getName().compareTo(o2.getName()):i;
}
});
for (Student s:students) {
System.out.println("s = " + s);
}
}
public static <T extends Comparable<T>> void sort(T[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j].compareTo(arr[j + 1]) > 0) {
T temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
public static <T> void sort(T[] arr, Comparator<T> c) {
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (c.compare(arr[j], arr[j + 1]) > 0) {
T temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
}