Java 泛型机制理解与实践
一. 泛型定义
泛型就是定义一种模板,例如ArrayList<T>
,然后在代码中为用到的类创建对应的ArrayList<类型>
,在Java
标准库中的ArrayList<T>
实现了List<T>
接口,它可以向上转型为List<T>
,Java泛型的主要目的是实现代码的重用性和类型安全性。假如没有泛型,我们可能就会定义很多遍ArrayList了,比如 ArrayList<Integer>
、ArrayList<String>
、ArrayList<Float>
等等。
二. 使用泛型
2.1 泛型类
使用ArrayList
时,如果不定义泛型类型时,泛型类型实际上就是Object
,此时,只能把<T>
当作Object
使用,没有发挥泛型的优势。
List list = new ArrayList(); // 编译器警告:
list.add("Hello");
list.add("World");
String first = (String) list.get(0); // 强制类型转换
String second = (String) list.get(1); // 强制类型转换
当我们定义泛型类型<String>
后,List<T>
的泛型接口变为强类型List<String>
List<String> list = new ArrayList<String>(); // 无编译器警告
// List<String> list = new ArrayList<>(); // 或者省略后面的String,编译器能自动推断出泛型类型
list.add("Hello");
list.add("World");
String first = list.get(0); // 无强制转型
String second = list.get(1); // 无强制转型
2.1 泛型接口
除了ArrayList<T>
使用了泛型,还可以在接口中使用泛型。例如Arrays.sort(Object[])
可以对任意数组进行排序,但待排序的元素必须实现Comparable<T>
这个泛型接口:
public interface Comparable<T> {
/**
* 返回-1: 当前实例比参数o小
* 返回 0: 当前实例与参数o相等
* 返回 1: 当前实例比参数o大
*/
int compareTo(T o);
}
如下代码表示实现了Comparable<Person>
接口的compareTo
方法。
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
Person[] ps = new Person[] {
new Person("Bob", 61),
new Person("Alice", 88),
new Person("Lily", 75),
};
Arrays.sort(ps);
System.out.println(Arrays.toString(ps));
}
}
class Person implements Comparable<Person> {
String name;
int score;
Person(String name, int score) {
this.name = name;
this.score = score;
}
public int compareTo(Person other) {
return this.name.compareTo(other.name);
}
public String toString() {
return this.name + "--" + this.score;
}
}
三. 自定义泛型
下面自定义了一个 Student 类,在创建 Student 对象的时候需要提供一个类型,看代码:
package org.example;
public class Main {
public static void main(String[] args) {
Student<String> stringStudent = new Student<String>("3");
Student<Integer> integerStudent = new Student<Integer>(3);
System.out.println("stringStudent = " + stringStudent);
System.out.println("integerStudent = " + integerStudent);
// anyTypeStudent 可以是 ?类型,此时相当于表示 Object
Student<?> anyTypeStudent = new Student<String>("309");
String grade = (String) anyTypeStudent.grade;
System.out.println("grade = " + grade);
Student.test1(99);
integerStudent.test2(9.9);
}
public static class Student<T> {
T grade;
Student(T grade) {
this.grade = grade;
}
@Override
public String toString() {
return "Student{" +
"grade=" + grade +
'}';
}
// 在方法定义前声明泛型
public static <E> void test1(E e) {
System.out.println(e);
}
// 在方法定义前声明泛型
public <E> void test2(E e) {
System.out.println(e);
}
}
}
四. 泛型界限
package org.example;
public class Score<T extends Number> { // 设定泛型上界,必须是Number的子类
private T score;
public Score(T score) {
this.score = score;
}
public T getScore() {
return score;
}
@Override
public String toString() {
return "Score{" +
"score=" + score +
'}';
}
public static void main(String[] args) {
// 只要是Number的子类即可,不能是 String
Score<Number> score1 = new Score<Number>(33);
// score2 的 T 类型必须是Integer的父类
Score<? super Integer> score2 = new Score<Number>(33);
// tmp 的类型必须是 Integer 父类:Number 或者 Object
Object tmp = score2.score;
}
}
4.1 定义泛型界限
在定义泛型时,如果要定义的泛型不止这个类型,可能是这个类型的父类、子类,可以用泛型界限实现:
- public class Score<T extends Number> {} :T类型必须是Number的子类
- public class Score<T super Integer> {} :T类型必须是Integer的父类
4.2 泛型引用界限
在定义泛型的引用时,也可以使用界限类型;
- Score<? extend Integer> score1; 必须为 Integer 或者其子类。
- Score<? super Integer> score2; 必须为 Integer 的父类。