1. 什么是泛型类?
泛型类是指类中使用了占位符类型(类型参数)的类。通过使用泛型类,你可以编写可以处理多种数据类型的代码,而无需为每种类型编写单独的类。泛型类使得代码更具通用性和可重用性,同时可以保证类型安全。
2. 泛型类的基本语法
2.1 泛型类的定义通常包含:
- 类型参数:通过尖括号
< >
指定,占位符通常是字母,比如T
、E
、K
、V
等。 - 类定义:类的成员变量、方法等可以使用类型参数。
2.2 泛型类的语法格式:
java">class ClassName<T> { // T 是泛型类型参数
// 类的成员变量
private T value;
// 泛型类的构造方法
public ClassName(T value) {
this.value = value;
}
// 泛型类的方法
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
3. 如何使用泛型类
在你定义好泛型类之后,你可以在实例化时指定具体的类型,告诉泛型类使用什么类型。
3.1 示例:使用泛型类
java">class Box<T> {
private T value; // 存储泛型类型的数据
// 构造方法
public Box(T value) {
this.value = value;
}
// 获取值的方法
public T getValue() {
return value;
}
// 设置值的方法
public void setValue(T value) {
this.value = value;
}
}
public class generic_type {
public static void main(String[] args) {
// 创建一个泛型类的对象,指定类型为 String
Box<String> stringBox = new Box<>("Hello");
System.out.println(stringBox.getValue()); // 输出 "Hello"
// 创建一个泛型类的对象,指定类型为 Integer
Box<Integer> intBox = new Box<>(123);
System.out.println(intBox.getValue()); // 输出 123
// 创建一个泛型类的对象,指定类型为 Double
Box<Double> doubleBox = new Box<>(45.67);
System.out.println(doubleBox.getValue()); // 输出 45.67
// 修改值
stringBox.setValue("Goodbye");
System.out.println(stringBox.getValue()); // 输出 "Goodbye"
}
}
3.2 代码解释:
-
Box<T>
:这是一个泛型类,类名是Box
,T
是类型参数。T
可以代表任何数据类型,具体的类型将在创建对象时指定。 -
private T value
:Box
类中定义了一个成员变量value
,它的数据类型是泛型T
,这意味着它可以存储任何类型的数据。 -
构造方法
public Box(T value)
:构造方法接收一个类型为T
的参数,表示初始化时将传入的数据赋值给value
。 -
getValue()
和setValue(T value)
:这两个方法分别用于获取和设置value
的值。它们的类型都是T
,所以可以支持任意类型的数据。
3.3 在 Main
类中的应用:
Box<String> stringBox = new Box<>("Hello");
:创建一个Box
类型的对象,指定T
为String
类型,初始化时给value
赋值为"Hello"
。System.out.println(stringBox.getValue());
:调用getValue()
方法输出"Hello"
。Box<Integer> intBox = new Box<>(123);
:创建一个Box
类型的对象,指定T
为Integer
类型,初始化时给value
赋值为123
。Box<Double> doubleBox = new Box<>(45.67);
:创建一个Box
类型的对象,指定T
为Double
类型,初始化时给value
赋值为45.67
。
4.扩展:将Box作为内部类和外部类的区别
4.1 将Box作为外部类(如上示例的代码就是将Box作为外部类使用的)
4.2 将Box作为内部类使用
错误示例:
java">public class generic_type {
class Box<T>{
private T value;
public Box(T value){
this.value = value;
}
public T getValue(){
return value;
}
public void setValue(T value){
this.value = value;
}
}
public static void main(String[] args){
Box<String> stringBox = new Box<>("Hello");//报错
System.out.println(stringBox.getValue());
Box<Integer> intBox = new Box<>(123);//报错
System.out.println(intBox.getValue());
Box<Double> doubleBox = new Box<>(45.67);//报错
System.out.println(doubleBox.getValue());
stringBox.setValue("Goodbye!");
System.out.println(stringBox.getValue());
}
}
为什么会出现这样的错误: 如果 Box
类定义在 generic_type
类内部,它是一个 成员内部类。Box
类只有在 generic_type
类的实例化对象中才有意义,且在外部无法直接使用 Box
类,必须通过 generic_type
的实例来访问它。
修改过后正确的内部类:
java">public class GenericType {
class Box<T> {
private T value;
public Box(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
public static void main(String[] args) {
// 必须先创建外部类 GenericType 的对象
GenericType genericType = new GenericType();
GenericType.Box<String> stringBox = genericType.new Box<>("Hello");
System.out.println(stringBox.getValue());
}
}
4.3 使用内部类和外部类的区别:
-
外部类定义
Box<T>
:- 独立性更强:
Box
类是独立的,可以单独作为一个类使用。比如,如果你在同一个包中的其他地方需要使用Box
类,就不需要创建generic_type
类的实例,可以直接创建Box
的实例。 - 访问范围更广:
Box
类的作用域是整个包内或者是类所在的文件中,其他地方可以直接使用它。
- 独立性更强:
-
内部类定义
Box<T>
:- 依赖于外部类:如果
Box
是generic_type
类的成员(内部类),那么必须先创建generic_type
类的对象,然后通过这个对象来创建Box
的实例。 - 访问范围较小:
Box
只能在generic_type
类内部或通过generic_type
的对象访问,限制了它的使用范围。其他类无法直接访问它,除非通过generic_type
类实例。
- 依赖于外部类:如果
5.泛型类的优点
- 代码复用:通过泛型类,你不需要为每种数据类型编写不同的类,节省了代码量。
- 类型安全:编译器可以在编译时检查类型是否匹配,避免了运行时错误和类型转换异常。
- 灵活性:同一个类可以处理不同类型的数据,例如
Box<String>
和Box<Integer>
。
6.泛型类的应用场景
6.1 应用场景
- 集合类:Java的集合框架(如
List
、Set
、Map
)广泛使用泛型类。你可以创建类型安全的集合。 - 容器类:泛型类常用于实现容器类,比如将某个类型的元素封装在类中。
- 工具类:很多通用的工具类,例如Java中的
Collections
类,也使用泛型来提高灵活性和类型安全。
6.2 示例:
6.2.1 使用泛型类实现一个集合类
java">// 泛型容器类 Container
public class Container<T> {
private T value;
public void setValue(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public static void main(String[] args) {
// 使用 Container 存储 Integer 类型数据
Container<Integer> intContainer = new Container<>();
intContainer.setValue(42);
System.out.println(intContainer.getValue()); // 输出 42
// 使用 Container 存储 String 类型数据
Container<String> strContainer = new Container<>();
strContainer.setValue("Hello");
System.out.println(strContainer.getValue()); // 输出 Hello
}
}
6.2.2 使用泛型类实现一个容器类
java">// 泛型类:容器类
class Container<T> {
private T value;
public void setValue(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
public class Main {
public static void main(String[] args) {
// 创建容器实例,用String类型
Container<String> stringContainer = new Container<>();
stringContainer.setValue("Hello");
System.out.println(stringContainer.getValue()); // 输出 Hello
// 创建容器实例,用Integer类型
Container<Integer> intContainer = new Container<>();
intContainer.setValue(100);
System.out.println(intContainer.getValue()); // 输出 100
}
}
6.2.3 使用泛型实现一个工具类
java">// 泛型工具类 GenericUtils
public class GenericUtils {
// 泛型方法:交换数组中的两个元素
public static <T> void swap(T[] array, int i, int j) {
T temp = array[i];
array[i] = array[j];
array[j] = temp;
}
// 泛型方法:打印数组元素
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.print(element + " ");
}
System.out.println();
}
public static void main(String[] args) {
// 使用泛型工具类处理 Integer 数组
Integer[] intArray = {1, 2, 3, 4};
swap(intArray, 0, 2);
printArray(intArray); // 输出:3 2 1 4
// 使用泛型工具类处理 String 数组
String[] strArray = {"apple", "banana", "cherry"};
swap(strArray, 0, 1);
printArray(strArray); // 输出:banana apple cherry
}
}
7. 泛型类的多个类型参数
7.1 解释
一个泛型类可以使用多个类型参数。例如,你可以定义一个包含键值对的容器类(如Map
),它就需要两个类型参数,一个表示键,一个表示值。
7.2 示例:使用多个类型参数
java">// 定义一个包含键值对的泛型类
class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
}
public class Main {
public static void main(String[] args) {
// 创建一个泛型Pair对象,指定键为String,值为Integer
Pair<String, Integer> pair = new Pair<>("age", 25);
System.out.println("Key: " + pair.getKey() + ", Value: " + pair.getValue());
}
}
8.泛型类的继承
8.1 解释
泛型类的继承 是指在 Java 中通过继承泛型类来创建新的类。继承泛型类时,可以选择固定泛型类型或保留泛型类型占位符。具体的做法取决于是否希望在子类中指定一个具体的类型,或者希望子类保持泛型类型。
8.2 泛型类继承类型
8.2.1 子类继承泛型类并保留泛型类型
如果我们希望在子类中保持泛型类型(即让子类继续使用泛型),可以通过在子类中指定泛型类型。
示例:子类继承泛型类并保留泛型类型
java">// 泛型父类
class Box<T> {
private T value;
public Box(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
// 泛型子类,保留父类的泛型类型
class ColoredBox<T> extends Box<T> {
private String color;
public ColoredBox(T value, String color) {
super(value);
this.color = color;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
public class Main {
public static void main(String[] args) {
// 使用 Integer 类型
ColoredBox<Integer> intBox = new ColoredBox<>(123, "Red");
System.out.println("Value: " + intBox.getValue() + ", Color: " + intBox.getColor());
// 使用 String 类型
ColoredBox<String> strBox = new ColoredBox<>("Hello", "Blue");
System.out.println("Value: " + strBox.getValue() + ", Color: " + strBox.getColor());
}
}
/*
输出:
Value: 123, Color: Red
Value: Hello, Color: Blue
*/
解释:
Box<T>
是一个泛型父类,表示一个可以存储任何类型T
的盒子。ColoredBox<T>
继承自Box<T>
,它保留了泛型T
,并增加了一个color
属性。- 在
main
方法中,我们通过创建ColoredBox<Integer>
和ColoredBox<String>
来使用这个泛型子类,分别存储整数和字符串。
8.2.2 子类继承泛型类并指定具体的类型
有时你希望子类继承泛型类并指定一个具体的类型。这时,可以在继承时直接指定泛型类型,而不保留泛型类型参数。你可以在子类中明确指定泛型类型(通常是常见的基本类型或子类型)。
示例:子类继承泛型类并指定具体类型
java">// 泛型父类
class Box<T> {
private T value;
public Box(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
// 子类继承泛型父类,并指定具体的类型
class IntegerBox extends Box<Integer> {
public IntegerBox(Integer value) {
super(value);
}
public void printSquaredValue() {
Integer value = getValue();
System.out.println("Squared value: " + (value * value));
}
}
public class Main {
public static void main(String[] args) {
// 使用 Integer 类型
IntegerBox integerBox = new IntegerBox(5);
System.out.println("Value: " + integerBox.getValue());
integerBox.printSquaredValue(); // 输出:Squared value: 25
}
}
/*
输出:
Value: 5
Squared value: 25
*/
解释:
Box<T>
是一个泛型父类,表示一个可以存储任何类型T
的盒子。IntegerBox
是一个子类,它继承了Box<Integer>
,并指定了T
为Integer
类型。IntegerBox
具有一个printSquaredValue
方法,演示如何使用从父类继承的getValue
方法。
8.2.3 子类继承泛型类并限制泛型的类型边界
你可以在子类中为泛型类型指定 边界(bounds),即限制泛型类型必须是某个特定类或接口的子类。例如,你可以让泛型类型 T
限定为 Number
或其子类。
示例:使用类型边界的泛型继承
java">// 泛型父类,限定 T 必须是 Number 的子类
class NumericBox<T extends Number> {
private T value;
public NumericBox(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
public void printSquare() {
System.out.println("Squared value: " + (value.doubleValue() * value.doubleValue()));
}
}
// 子类继承泛型父类,并指定具体类型
class IntegerBox extends NumericBox<Integer> {
public IntegerBox(Integer value) {
super(value);
}
}
class DoubleBox extends NumericBox<Double> {
public DoubleBox(Double value) {
super(value);
}
}
public class Main {
public static void main(String[] args) {
// 使用 Integer 类型
IntegerBox integerBox = new IntegerBox(5);
integerBox.printSquare(); // 输出:Squared value: 25.0
// 使用 Double 类型
DoubleBox doubleBox = new DoubleBox(3.14);
doubleBox.printSquare(); // 输出:Squared value: 9.8596
}
}
/*
输出:
Squared value: 25.0
Squared value: 9.8596
*/
解释:
NumericBox<T extends Number>
是一个带有类型边界的泛型类,限定了T
必须是Number
或其子类。IntegerBox
和DoubleBox
分别继承NumericBox<Integer>
和NumericBox<Double>
,实现了不同类型的继承。- 在
printSquare
方法中,调用了value.doubleValue()
,这在Number
类中是定义好的,因此可以安全地对数值进行平方运算。
8.3 注意事项
- 泛型类型的擦除: 泛型类型在编译时会进行类型擦除(type erasure),所以在运行时泛型类型会被替换为它的边界类型(例如
Object
或Number
)。这就是为什么泛型类不能直接创建数组。 - 泛型和继承: 泛型类和子类之间的继承关系可以保留泛型类型,也可以指定具体的类型。你可以通过继承来扩展泛型类的功能,但要注意类型边界的使用。
- 类型安全: 泛型的使用保证了类型安全,通过编译时的类型检查,避免了运行时类型错误,减少了类型强制转换的需要。