使用ArrayList时,如果不定义泛型类型时,泛型类型实际上就是Object

  1. // 编译器警告:
  2. List list = new ArrayList();
  3. list.add("Hello");
  4. list.add("World");
  5. String first = (String) list.get(0);
  6. String second = (String) list.get(1);

此时,只能把<T>当作Object使用,没有发挥泛型的优势。

当我们定义泛型类型<String>后,List<T>的泛型接口变为强类型List<String>

  1. // 无编译器警告:
  2. List<String> list = new ArrayList<String>();
  3. list.add("Hello");
  4. list.add("World");
  5. // 无强制转型:
  6. String first = list.get(0);
  7. String second = list.get(1);

当我们定义泛型类型<Number>后,List<T>的泛型接口变为强类型List<Number>

  1. List<Number> list = new ArrayList<Number>();
  2. list.add(new Integer(123));
  3. list.add(new Double(12.34));
  4. Number first = list.get(0);
  5. Number second = list.get(1);

编译器如果能自动推断出泛型类型,就可以省略后面的泛型类型。例如,对于下面的代码:

  1. List<Number> list = new ArrayList<Number>();

编译器看到泛型类型List<Number>就可以自动推断出后面的ArrayList<T>的泛型类型必须是ArrayList<Number>,因此,可以把代码简写为:

  1. // 可以省略后面的Number,编译器可以自动推断泛型类型:
  2. List<Number> list = new ArrayList<>();

泛型接口

除了ArrayList<T>使用了泛型,还可以在接口中使用泛型。例如,Arrays.sort(Object[])可以对任意数组进行排序,但待排序的元素必须实现Comparable<T>这个泛型接口:

  1. public interface Comparable<T> {
  2. /**
  3. * 返回负数: 当前实例比参数o小
  4. * 返回0: 当前实例与参数o相等
  5. * 返回正数: 当前实例比参数o大
  6. */
  7. int compareTo(T o);
  8. }

可以直接对String数组进行排序:

  1. // sort
  2. import java.util.Arrays;
  3. public class Main {
  4. public static void main(String[] args) {
  5. String[] ss = new String[] { "Orange", "Apple", "Pear" };
  6. Arrays.sort(ss);
  7. System.out.println(Arrays.toString(ss));
  8. }
  9. }

这是因为String本身已经实现了Comparable<String>接口。如果换成我们自定义的Person类型试试:

  1. // sort
  2. import java.util.Arrays;
  3. public class Main {
  4. public static void main(String[] args) {
  5. Person[] ps = new Person[] {
  6. new Person("Bob", 61),
  7. new Person("Alice", 88),
  8. new Person("Lily", 75),
  9. };
  10. Arrays.sort(ps);
  11. System.out.println(Arrays.toString(ps));
  12. }
  13. }
  14. class Person {
  15. String name;
  16. int score;
  17. Person(String name, int score) {
  18. this.name = name;
  19. this.score = score;
  20. }
  21. public String toString() {
  22. return this.name + "," + this.score;
  23. }
  24. }

运行程序,我们会得到ClassCastException,即无法将Person转型为Comparable。我们修改代码,让Person实现Comparable<T>接口:

  1. // sort
  2. import java.util.Arrays;
  3. public class Main {
  4. public static void main(String[] args) {
  5. Person[] ps = new Person[] {
  6. new Person("Bob", 61),
  7. new Person("Alice", 88),
  8. new Person("Lily", 75),
  9. };
  10. Arrays.sort(ps);
  11. System.out.println(Arrays.toString(ps));
  12. }
  13. }
  14. class Person implements Comparable<Person> {
  15. String name;
  16. int score;
  17. Person(String name, int score) {
  18. this.name = name;
  19. this.score = score;
  20. }
  21. public int compareTo(Person other) {
  22. return this.name.compareTo(other.name);
  23. }
  24. public String toString() {
  25. return this.name + "," + this.score;
  26. }
  27. }

运行上述代码,可以正确实现按name进行排序。

也可以修改比较逻辑,例如,按score从高到低排序。请自行修改测试。

小结

使用泛型时,把泛型参数<T>替换为需要的class类型,例如:ArrayList<String>ArrayList<Number>等;

可以省略编译器能自动推断出的类型,例如:List<String> list = new ArrayList<>();

不指定泛型参数类型时,编译器会给出警告,且只能将<T>视为Object类型;

可以在接口中定义泛型类型,实现此接口的类必须实现正确的泛型类型。