文章目录
- 什么是泛型?
- 泛型的声明
- 泛型的使用
- 泛型方法
- 通配符和泛型上下界
- 1)通配符
- 2)泛型上下界
- 泛型的好处
- 注意事项
泛型提供了一种在编写代码时更好地 支持类型安全的机制。通过泛型,我们可以编写更加 通用、 灵活、 可读性高的代码,同时 减少类型转换和运行时错误。
什么是泛型?
泛型(Generics)是一种参数化类型的概念
它使得我们可以编写能够适用于多种类型的代码,而不是为每种类型都写一份代码。泛型提供了编译时的类型检查,使得代码更加安全,并且减少了在运行时进行频繁的类型转换
例如:
我们之前有为很多箱子都贴上了固定的标签!
装 苹果,老鼠,小狗,因为不能放一起呀!虽然,狗现在已经不多管闲事去拿耗子啦_
有多少种类就需要多少箱子,新增加一个就需要再增加,就很麻烦
于是我们想到了,箱子不贴固定的名称,来一种就装一种,这样就不需要为每一种单独定义一个标签啦
之前的 苹果,老鼠,小狗等等箱子,就是具体的类型。而后来的 东西,就是一个泛型。
泛型的声明
在 Java 中,泛型主要通过在类
、接口
、方法
中使用泛型类型参数来实现。
下面就使用泛型去定义一个箱子
// 定义一个泛型类(比如这就是一个装东西的箱子)
class Box<T> {
private T value;
public void setValue(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
上述代码中,Box
类使用了泛型类型参数 T
,使得这个类可以存储和返回任意类型的数据。
泛型的使用
使用上面已经定义好的箱子去装各种各样的东西
先往里装一种类型的东西,如老鼠
public static void main(String[] args) {
// 可以往箱子里装 int 型数字(比如这就是老鼠)
Box<Integer> integerBox = new Box<>();
integerBox.setValue(42);
int intValue = integerBox.getValue();
System.out.println(intValue);
}
输出结果:42
再往里装另一种类型的东西,如小狗
public static void main(String[] args) {
// 还可以往箱子里装 String 型的字符(比如这就是小狗)
Box<String> stringBox = new Box<>();
stringBox.setValue("哈士奇");
String stringValue = stringBox.getValue();
System.out.println(stringValue);
}
输出结果:哈士奇
泛型方法
除了泛型类,Java 还支持泛型方法。泛型方法可以在普通类中定义,也可以在泛型类中定义。
以下是一个简单的泛型方法的例子:
// 定义一个泛型方法
public static <E> void printBoxSaveWhat(E e) {
System.out.println("箱子里装的是: " + e);
}
// 使用泛型方法
public static void main(String[] args) {
// 打印第一种类型
printBoxSaveWhat(42);
// 打印外一种类型
printBoxSaveWhat("哈士奇");
}
输出结果:
箱子里装的是: 42
箱子里装的是: 哈士奇
上述代码中,printBoxSaveWhat
方法是一个泛型方法,可以接受任意类型的参数。在使用时,编译器会根据传入的实际参数类型进行类型推断。
通配符和泛型上下界
Java 泛型还引入了通配符和泛型上下界的特性。
1)通配符
用于表示未知类型
示例代码:
// 使用通配符
public static void printValues(List<?> values) {
for (Object value : values) {
System.out.println(value);
}
}
public static void main(String[] args) {
System.out.println("开始打印 整数");
List<Integer> integers = Arrays.asList(1, 2, 3);
printValues(integers);
System.out.println("开始打印 小数");
List<Double> doubles = Arrays.asList(1.1, 2.2);
printValues(doubles);
}
输出结果:
开始打印 整数
1
2
3
开始打印 小数
1.1
2.2
在上述代码中,printValues
方法使用了通配符 ?
,允许接受任意类型的 List。
2)泛型上下界
用于限定泛型类型的范围
示例代码:
// 使用泛型上下界
public static <T extends Number> double sum(List<T> numbers) {
double total = 0;
for (T number : numbers) {
total += number.doubleValue();
}
return total;
}
public static void main(String[] args) {
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5);
System.out.println("整数的加和结果是: " + sum(integers));
List<Double> doubles = Arrays.asList(1.1, 2.2, 3.3, 4.4);
System.out.println("小数的加和结果是: " + sum(doubles));
}
输出结果:
整数的加和结果是: 15.0
小数的加和结果是: 11.0
在上述代码中, sum
方法使用了泛型上下界 T extends Number
,表示只能接受 Number 类型或其子类型
泛型的好处
- 类型安全: 泛型提供了编译时的类型检查,避免了在运行时发生类型错误的可能性。
- 代码复用: 泛型允许编写通用的代码,适用于多种数据类型,提高了代码的复用性。
- 可读性和可维护性: 使用泛型能够使代码更加清晰、简洁,提高了代码的可读性和可维护性。
注意事项
- 类型擦除: 泛型在编译时会进行类型擦除,即泛型信息在运行时被擦除,转换为原始类型。这可能导致一些限制,例如不能直接创建泛型数组。
- 通配符限制: 使用通配符
<?>
时,只能读取,无法修改泛型集合中的元素。如果需要修改,可以使用<? extends T>
或<? super T>
。 - 泛型和继承: 泛型不支持协变(covariant)和逆变(contravariant)。例如,
List
不是List
的子类型。 - 原始类型和泛型混用: 尽量避免在泛型代码中使用原始类型,以保持类型安全。
用通配符 <?>
时,只能读取,无法修改泛型集合中的元素。如果需要修改,可以使用 <? extends T>
或 <? super T>
。
- 泛型和继承: 泛型不支持协变(covariant)和逆变(contravariant)。例如,
List
不是List
的子类型。 - 原始类型和泛型混用: 尽量避免在泛型代码中使用原始类型,以保持类型安全。
在实际编程中,合理利用泛型可以使代码更加健壮、灵活,但也需要注意一些泛型的特性和限制。通过了解和熟练使用泛型,可以写出更加清晰、安全和可维护的 Java 代码。