12.5.1 什么是类型通配符

背景

  • 当声明一个变量/形参时,这个变量/形参的类型是一个泛型类或泛型接口,例如:Comparator 类型
  • 但是仍然无法确定这个泛型类或泛型接口的类型变量的具体类型,此时考虑使用类型通配符

简介

  • 类型通配符一般是使用?代替具体的类型实参
  • 所以,类型通配符是类型实参,而不是类型形参

12.5.2 <?> 任意类型

学生类

  1. @Data
  2. @AllArgsConstructor
  3. @NoArgsConstructor
  4. class StudentType<T> {
  5. private String name;
  6. private T score;
  7. }

测试类

@Test
void test() {
    // 语文老师使用时:
    StudentType<String> stu1 = new StudentType<>("张三", "良好");

    // 数学老师使用时:
    StudentType<Double> stu2 = new StudentType<>("张三", 90.5);

    // 英语老师使用时:
    StudentType<Character> stu3 = new StudentType<>("张三", 'C');

    StudentType<?>[] stuList = {stu1, stu2, stu3};

    for (StudentType<?> studentType : stuList) {
        System.out.println(studentType);
    }
}

12.5.2 类型通配符的上限

语法格式

<? extends 实参类型上限>

要求该泛型的类型,只能是实参类型,或实参类型的子类类型

实际栗子

要声明一个学生管理类,这个管理类要包含一个方法,找出学生数组中成绩最高的学生对象。
要求学生的成绩的类型必须可比较大小,实现 Comparable 接口

学生管理类

class StudentService {
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public static Student<? extends Comparable> max(Student<? extends Comparable>[] arr){
        Student<? extends Comparable> max = arr[0];
        for (int i = 0; i < arr.length; i++) {
            if(arr[i].getScore().compareTo(max.getScore())>0){
                max = arr[i];
            }
        }
        return max;
    }
}

测试类

public class TestGeneric {
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public static void main(String[] args) {
        Student<? extends Double>[] arr = new Student[3];
        arr[0] = new Student<Double>("张三", 90.5);
        arr[1] = new Student<Double>("李四", 80.5);
        arr[2] = new Student<Double>("王五", 94.5);

        Student<? extends Comparable> max = StudentService.max(arr);
        System.out.println(max);
    }
}

12.5.3 类型通配符的下限

语法格式

<? super 实参类型下限>

要求该泛型的类型,只能是实参类型,或实参类型的父类类型

现在要声明一个数组工具类,包含可以给任意对象数组进行从小到大排序,只要你指定定制比较器对象,而且这个定制比较器对象可以是当前数组元素类型自己或其父类的定制比较器对象

数组工具类:

class MyArrays{
    public static <T> void sort(T[] arr, Comparator<? super T> c){
        for (int i = 1; i < arr.length; i++) {
            for (int j = 0; j < arr.length-i; j++) {
                if(c.compare(arr[j], arr[j+1])>0){
                    T temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
    }
}

例如:有如下JavaBean

class Person{
    private String name;
    private int age;
    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    public Person() {
        super();
    }
    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 "name=" + name + ", age=" + age;
    }
}
class Student extends Person{
    private int score;

    public Student(String name, int age, int score) {
        super(name, age);
        this.score = score;
    }

    public Student() {
        super();
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    @Override
    public String toString() {
        return super.toString() + ",score=" + score;
    }

}

测试类

public class TestGeneric {
    public static void main(String[] args) {
        Student[] all = new Student[3];
        all[0] = new Student("张三", 23, 89);
        all[1] = new Student("李四", 22, 99);
        all[2] = new Student("王五", 25, 67);

        MyArrays.sort(all, new Comparator<Person>() {

            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge() - o2.getAge();
            }
        });

        System.out.println(Arrays.toString(all));

        MyArrays.sort(all, new Comparator<Student>() {

            @Override
            public int compare(Student o1, Student o2) {
                return o1.getScore() - o2.getScore();
            }
        });
        System.out.println(Arrays.toString(all));
    }
}

12.5.4 使用类型通配符来指定类型参数的问题

    stu1.setScore(null);//除了null,无法设置为其他值


    Student<? extends Number> stu2 = new Student<>();
    stu2.setScore(null);//除了null,无法设置为其他值

    Student<? super Number> stu3 = new Student<>();
    stu3.setScore(56);//可以设置Number或其子类的对象
}

}
class Student{
private String name;
private T score;

public Student() {
    super();
}
public Student(String name, T score) {
    super();
    this.name = name;
    this.score = score;
}
public String getName() {
    return name;
}
public void setName(String name) {
    this.name = name;
}
public T getScore() {
    return score;
}
public void setScore(T score) {
    this.score = score;
}
@Override
public String toString() {
    return "姓名:" + name + ", 成绩:" + score;
}

}


## 12.5 练习

在数组工具类中声明如下泛型方法:

(1)可以在任意类型的对象数组中,查找某个元素的下标,按照顺序查找,如果有重复的,就返回第一个找到的,如果没有返回-1

(2)可以在任意类型的对象数组中,查找最大值,要求元素必须实现Comparable接口

(3)可以在任意类型的对象数组中,查找最大值,按照指定定制比较器来比较元素大小

(4)可以给任意对象数组进行从小到大排序,要求数组元素类型必须实现Comparable接口

(5)可以给任意对象数组进行从小到大排序,只要你指定定制比较器对象,不要求数组元素实现Comparable接口

(6)可以将任意对象数组的元素拼接为一个字符串返回

```java
public class MyArrays {
    //可以在任意类型的对象数组中,查找某个元素的下标,按照顺序查找,如果有重复的,就返回第一个找到的,如果没有返回-1
    public static <T> int find(T[] arr, T value) {
        for (int i = 0; i < arr.length; i++) {
            if(arr[i].equals(value)) {//使用==比较太严格,使用equals方法,因为任意对象都有equals方法
                return i;
            }
        }
        return -1;
    }

    //可以在任意类型的对象数组中,查找最大值,要求元素必须实现Comparable接口
    public static <T extends Comparable<? super T>> T max(T[] arr) {
        T max = arr[0];
        for (int i = 0; i < arr.length; i++) {
            if(max.compareTo(arr[i])<0) {//if(max < arr[i]) {
                max = arr[i];
            }
        }
        return max;
    }

    //可以在任意类型的对象数组中,查找最大值,按照指定定制比较器来比较元素大小
    public static <T> T max(T[] arr, Comparator<? super T> c) {
        T max = arr[0];
        for (int i = 0; i < arr.length; i++) {
            if(c.compare(max, arr[i])<0) {//if(max < arr[i]) {
                max = arr[i];
            }
        }
        return max;
    }

    //可以给任意对象数组进行从小到大排序,要求数组元素类型必须实现Comparable接口
    public static <T extends Comparable<? super T>> void sort(T[] arr) {
        for (int i = 0; i < arr.length-1; i++) {
            int minIndex = i;
            for (int j = i+1; j < arr.length; j++) {
                if(arr[minIndex].compareTo(arr[j])>0) {
                    minIndex = j;
                }
            }
            if(minIndex!=i) {
                T temp = arr[minIndex];
                arr[minIndex] = arr[i];
                arr[i] = temp;
            }
        }
    }

    //可以给任意对象数组进行从小到大排序,只要你指定定制比较器对象,不要求数组元素实现Comparable接口
    public static <T> void sort(T[] arr, Comparator<? super T> c) {
        for (int i = 0; i < arr.length-1; i++) {
            int minIndex = i;
            for (int j = i+1; j < arr.length; j++) {
                if(c.compare(arr[minIndex],arr[j])>0) {
                    minIndex = j;
                }
            }
            if(minIndex!=i) {
                T temp = arr[minIndex];
                arr[minIndex] = arr[i];
                arr[i] = temp;
            }
        }
    }

    //可以将任意对象数组的元素拼接为一个字符串返回
    public static <T> String toString(T[] arr) {
        String str = "[";
        for (int i = 0; i < arr.length; i++) {
            if(i==0) {
                str += arr[i];
            }else {
                str += "," + arr[i];
            }
        }
        str += "]";
        return str;
    }
}