Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。它允许在定义类、接口和方法时使用类型参数。这种技术使得在编译期间可以使用任何类型,而不是仅限于特定的类型。这大大提高了代码的灵活性和可重用性。
泛型的基本语法是在类、接口或方法名的后面加上尖括号(< >),并在其中定义类型参数。例如:
public class Box {
private T t;
public void set(T t) {
this.t = t;
}
public T get() {
return t;
}
}
在上述代码中,T 是一个类型参数,可以代表任何类型。你可以在类的定义中使用 T 来表示泛型,然后在方法中使用 T 来表示任何类型的对象。
在定义泛型类或方法时,使用类型参数代替具体的类型。类型参数在使用前需要命名,通常使用大写字母来表示。例如,在上述的 Box 类中,我们使用 T 作为类型参数。
java 中泛型标记符:
Java 泛型的类型参数有限制,不能是 void、boolean、char、byte、short、int、float、double等原始类型。此外,Java 泛型的类型参数只能是类类型(包括 Class、接口类型或枚举类型),不能是数组类型或函数类型。
除了具体类型作为类型参数外,Java 泛型还支持通配符类型参数。通配符类型参数表示可以接受任何类型的参数,例如 List> 表示可以接受任何类型的 List。通配符类型参数还有两种限定符:
除了泛型类外,还可以定义泛型方法。泛型方法允许在方法的返回值和参数类型中使用类型参数。例如:
public T getFirst(List list) {
return list.get(0);
}
在上述代码中,我们定义了一个泛型方法 getFirst,它接受一个 List
当一个类继承一个泛型类时,子类可以选择是否继续使用父类的类型参数。例如:
public class MyBox extends Box {
// ...
}
在上述代码中,我们定义了一个子类 MyBox,它继承了 Box
Java 泛型的类型信息在编译后的代码中会被擦除,只剩下原始类型的信息。这是为了保持 Java 代码的兼容性和跨平台性。因此,在使用泛型时,不能在运行时通过反射来获取泛型的具体类型信息。
以下是一个简单代码示例,演示了类型擦除的概念:
public class MyClass {
// 定义一个泛型类MyClass,其中T是一个类型参数
private T value; // 定义一个私有成员变量value,它的类型是泛型参数T
public MyClass(T value) { // 定义一个构造函数,接受一个类型为T的参数
this.value = value; // 将参数值赋值给成员变量value
}
public T getValue() { // 定义一个公共方法getValue,返回类型为T的值
return value; // 返回成员变量value的值
}
public static void main(String[] args) {
// 创建两个不同类型的实例
MyClass intExample = new MyClass<>(123); // 创建一个Integer类型的MyClass实例,并将其引用赋给intExample变量
MyClass stringExample = new MyClass<>("Hello"); // 创建一个String类型的MyClass实例,并将其引用赋给stringExample变量
// 调用getValue方法并打印结果
System.out.println(intExample.getValue()); // 调用intExample的getValue方法并打印返回值,输出123
System.out.println(stringExample.getValue()); // 调用stringExample的getValue方法并打印返回值,输出Hello
}
}
Java 泛型还支持有界类型参数。有界类型参数允许在定义泛型类或方法时,对类型参数进行限制。例如:
public class MyList {
private List list;
// ...
}
在上述代码中,我们定义了一个泛型类 MyList,其中的类型参数 T 必须是 Number 或其子类。这样,在使用 MyList 时,只能使用符合这个条件的类型作为类型参数。
import java.util.ArrayList;
import java.util.List;
// 定义一个泛型类 User,使用通配符类型参数
class User {
// 定义一个存储对象引用的列表,其中T代表任意类型
List friends;
// 构造函数,初始化朋友列表
public User() {
this.friends = new ArrayList<>();
}
// 泛型方法,接受一个类型为T的参数并返回一个类型为T的对象
public T getLastFriend() {
// 返回列表中的最后一个朋友,如果列表为空则返回null
if (friends.isEmpty()) {
return null;
} else {
return friends.get(friends.size() - 1);
}
}
// 重写toString方法,用于打印用户和朋友列表
@Override
public String toString() {
return "User{" +
"friends=" + friends +
'}';
}
}
// 定义一个子类 StringUser,继承自泛型类 User,并指定类型参数为 String
class StringUser extends User {
// 重写父类的toString方法,只打印字符串类型的朋友列表
@Override
public String toString() {
return "StringUser{" +
"friends=" + friends +
'}';
}
}
// 主类,测试以上定义的泛型类和子类
public class MyClass {
public static void main(String[] args) {
// 创建一个 User 对象,存储字符串类型的朋友
User user = new User<>();
((User) user).friends.add("Alice");
((User) user).friends.add("Bob");
System.out.println(user); // 输出:User{friends=[Alice, Bob]}
System.out.println(user.getLastFriend()); // 输出:Bob
// 创建一个 StringUser 对象,同样存储字符串类型的朋友
StringUser stringUser = new StringUser();
stringUser.friends.add("Charlie");
stringUser.friends.add("Dave");
System.out.println(stringUser); // 输出:StringUser{friends=[Charlie, Dave]}
System.out.println(stringUser.getLastFriend()); // 输出:Dave
}
}
代码解释: