使用泛型
约 879 字大约 3 分钟
2025-07-17
泛型的主要目的是在编译期进行类型检查,避免运行时的 ClassCastException
,并提高代码的复用性。
2.1 泛型的基本使用
在使用 ArrayList
等集合类时,如果未定义泛型类型,那么泛型类型实际上会被视为 Object
。这意味着你可以向 ArrayList
中添加任何类型的对象,但在取出对象时需要进行强制类型转换。
// 编译器警告:
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<String> list = new ArrayList<String>();
list.add("Hello");
list.add("World");
// 无强制转型:
String first = list.get(0);
String second = list.get(1);
类似的,当我们定义泛型类型为 <Number>
后,List<T>
的泛型接口变为强类型 List<Number>
,可以存储 Number
及其子类的实例。
List<Number> list = new ArrayList<Number>();
list.add(new Integer(123));
list.add(new Double(12.34));
Number first = list.get(0);
Number second = list.get(1);
Java 编译器具有类型推断能力,在某些情况下可以省略后面的泛型类型。例如:
List<Number> list = new ArrayList<>(); // 可以省略后面的 Number,编译器可以自动推断泛型类型
2.2 泛型接口
除了 ArrayList<T>
之外,还可以在接口中使用泛型。例如,Arrays.sort(Object[])
可以对任意数组进行排序,但待排序的元素必须实现 Comparable<T>
这个泛型接口。Comparable<T>
接口定义了对象之间比较大小的方法。
public interface Comparable<T> {
/**
* 返回负数: 当前实例比参数o小
* 返回0: 当前实例与参数o相等
* 返回正数: 当前实例比参数o大
*/
int compareTo(T o);
}
String
类本身已经实现了 Comparable<String>
接口,因此可以直接对 String
数组进行排序。
// sort
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
String[] ss = new String[] { "Orange", "Apple", "Pear" };
Arrays.sort(ss);
System.out.println(Arrays.toString(ss));
}
}
但如果尝试对自定义的 Person
类型数组进行排序,则会抛出 ClassCastException
,因为 Person
类没有实现 Comparable
接口。
// sort
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); // 抛出 ClassCastException
System.out.println(Arrays.toString(ps));
}
}
class Person {
String name;
int score;
Person(String name, int score) {
this.name = name;
this.score = score;
}
public String toString() {
return this.name + "," + this.score;
}
}
为了解决这个问题,我们需要让 Person
类实现 Comparable<Person>
接口,并实现 compareTo
方法来定义 Person
对象之间的比较规则。
// sort
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); // 按 name 排序
}
public String toString() {
return this.name + "," + this.score;
}
}
在上述代码中,compareTo
方法使用 name
属性进行比较,因此 Arrays.sort(ps)
会按照 name
的字母顺序对 Person
对象进行排序。 当然,你也可以修改 compareTo
方法的实现,按照 score
等其他属性进行排序。