泛型是Java中一种强大的特性,它允许我们在定义类、接口或方法时使用类型参数,从而使代码更加灵活和可重用。泛型类是指具有一个或多个类型参数的类,这些类型参数在类定义时并不明确,而是在使用时才确定具体的类型。
泛型类的基本定义
我们可以通过以下方式定义一个泛型类:
public class Score<T> { // 泛型类需要使用<>,并在其中添加1 - N个类型变量
String name;
String id;
T value; // T会根据使用时提供的类型自动变成对应类型
public Score(String name, String id, T value) { // 这里T可以是任何类型,但是一旦确定,就不能修改了
this.name = name;
this.id = id;
this.value = value;
}
}
在这个例子中,T
是一个类型参数,它可以是任何类型。当我们创建 Score
类的实例时,需要指定 T
的具体类型。
泛型类的使用
我们可以通过以下方式使用泛型类:
public static void main(String[] args) {
Score<String> score = new Score<>("计算机", "6358", "斯国一");
// 因为现在有了类型变量,在使用时同样需要跟上<>并在其中填写明确要使用的类型
// 这样我们就可以根据不同的类型进行选择了
String value = score.value; // 一旦类型明确,那么泛型就变成对应的类型了
System.out.println(value);
}
在这个例子中,我们创建了一个 Score<String>
的实例,这意味着 T
被指定为 String
类型。因此,value
字段的类型也是 String
。
泛型类的类型推断
从Java 7开始,我们可以在创建泛型类实例时使用“钻石运算符”(<>
),让编译器自动推断类型参数:
Score<String> score = new Score<>("计算机", "6358", "斯国一");
泛型类的限制
1. 静态方法中不能使用泛型类型参数
由于泛型类型参数是在实例化时确定的,而静态方法属于类级别,不依赖于实例,因此在静态方法中不能使用泛型类型参数:
public class Test<T> {
// 错误:静态方法中不能使用泛型类型参数
public static void print(T value) {
System.out.println(value);
}
}
2. 泛型类型参数默认是 Object
类型
在泛型类中,如果不明确指定类型参数的具体类型,那么默认会将其视为 Object
类型:
public void test(T t) {
Object obj = t; // 默认情况下,t 被视为 Object 类型
}
3. 不能直接创建泛型类型的实例或数组
由于类型参数在编译时是不确定的,因此不能直接创建泛型类型的实例或数组:
public class Test<T> {
// 错误:不能直接创建泛型类型的实例
T obj = new T();
// 错误:不能直接创建泛型类型的数组
T[] array = new T[10];
}
4. 泛型类型参数不能是基本类型
泛型类型参数必须是引用类型,不能是基本类型(如 int
、char
等)。如果需要使用基本类型,可以使用对应的包装类:
public class Test<T> {
public T value;
}
public static void main(String[] args) {
Test<Integer> test = new Test<>(); // 使用 Integer 代替 int
}
5. 泛型类型参数可以是数组类型
虽然泛型类型参数不能是基本类型,但可以是基本类型的数组,因为数组本身是引用类型:
public static void main(String[] args) {
Test<int[]> test = new Test<>(); // 使用 int[] 作为泛型类型参数
}
多类型参数的泛型类
泛型类可以定义多个类型参数,多个类型参数之间用逗号隔开:
public class Test<A, B, C> { // 多个类型变量使用逗号隔开
public A a;
public B b;
public C c;
}
在使用时,需要为每个类型参数指定具体的类型:
public static void main(String[] args) {
Test<String, Integer, Character> test = new Test<>(); // 使用钻石运算符可以省略其中的类型
test.a = "hello";
test.b = 10;
test.c = '测';
}
通配符的使用
在某些情况下,我们希望某个变量能够引用任意类型的泛型对象,这时可以使用通配符 ?
:
public static void main(String[] args) {
Test<?> test = new Test<Integer>();
test = new Test<String>();
Object o = test.value; // 使用通配符时,具体类型会变成 Object
}
通配符 ?
表示未知类型,它可以匹配任何类型。但由于类型不确定,因此在使用通配符时,泛型类型会被视为 Object
类型。
总结
泛型类是Java中非常强大的工具,它允许我们在编写代码时保持类型的灵活性,同时又能确保类型安全。通过使用泛型,我们可以编写出更加通用和可重用的代码。然而,泛型也有一些限制,例如不能在静态方法中使用泛型类型参数、不能直接创建泛型类型的实例或数组等。理解这些限制并合理使用泛型,可以帮助我们编写出更加健壮和高效的代码。