泛型
Java中的泛型(Generics)是一种允许在类、接口和方法中使用类型参数的特性。极大地提升了代码的类型安全性和可读性,同时赋予了程序员更高的抽象能力。
泛型的核心思想是参数化类型,即把类型当作参数一样传入类或方法中,从而让类或方法能够适用于多种数据类型
起源:集合中的元素类型在编译时无法得到严格的检查,这导致了潜在的类型不匹配错误,这些错误往往只能在运行时通过异常暴露。泛型的引入正是为了解决这一问题,泛型提高了代码的健壮性和可维护性。
Java中的泛型在编译阶段会被擦除,实际生成的字节码并不包含泛型信息,而是将所有的泛型参数替换为它们的限定类型(如果没有限定,则为Object)。这意味着运行时不存在泛型类型的信息,所有类型检查都在编译时完成。
(因此泛型可以被认为是“更高级的语法糖”,因为它在不改变语言功能的前提下,显著提升了编写代码的便捷性和安全性,使程序员能够以更加类型安全和简洁的方式处理各种数据类型。)
语法糖的定义是:在计算机科学中,语法糖(Syntactic sugar)指的是编程语言中那些没有改变语言表达能力但能够使代码更易阅读、编写或表达更直观的语法构造。它们通常是对语言已有功能的更便捷、更友好的表达形式,使程序员能够以更符合人类思维习惯的方式编写代码,而不引入新的计算能力。简而言之,语法糖是为了提高代码的可读性和开发效率而设计的,虽然不是语言功能上的必需,但对提升编程体验至关重要。
泛型的声明定义使用:
// 定义一个泛型类 Box,其中 T 表示类型参数
public class Box<T> {
private T item;
public void setItem(T item) {
this.item = item;
}
public T getItem() {
return item;
}
}
// 使用泛型类
public class Main {
public static void main(String[] args) {
// 创建一个可以存储 String 类型的 Box 对象
Box<String> stringBox = new Box<>();
stringBox.setItem("Hello World");
System.out.println(stringBox.getItem()); // 输出: Hello World
// 创建一个可以存储 Integer 类型的 Box 对象
Box<Integer> integerBox = new Box<>();
integerBox.setItem(42);
System.out.println(integerBox.getItem()); // 输出: 42
}
}
使用泛型前:
ArrayList list = new ArrayList();
list.add("Hello");
String str = (String) list.get(0); // 需要手动类型转换
使用泛型后:
ArrayList<String> list = new ArrayList<String>();
list.add("Hello");
String str = list.get(0); // 不需要类型转换
泛型中,extends
和?
(通配符)是两个关键概念:
当使用extends
时,可以指定泛型参数必须继承自特定的类型或实现特定的接口。
定义了一个泛型类NumberPrinter
,其泛型参数T
必须是Number
或其子类:
public class NumberPrinter<T extends Number> {
public void print(T number) {
System.out.println(number.doubleValue());
}
}
这样,NumberPrinter
的实例可以接受Integer
、Double
等Number
的子类作为参数,但不能接受非Number
类型的对象。
?
是通配符,它有两种形式:无界通配符(?
)和上界通配符(? extends T
)。当使用? extends T
时,表示可以接受任何T
类型或其子类的实例。
上界通配符👇(为了限定范围)
List<? extends Number> numbers = new ArrayList<Integer>(); // 合法,Integer是Number的子类
无界通配符👇(当你不关心具体类型,只想表明这是一个泛型类型,或者在多种类型之间需要通用性时使用。无界通配符常用于方法参数,以接受各种泛型类型的集合,但在这种情况下,除了读取操作,对集合的修改受到限制。)
List<?> anyList = new ArrayList<String>(); // 可以指向任何类型的List
的存在不是为了完全放弃类型检查,而是提供一种机制,在保持一定类型安全性的基础上,增加代码的灵活性和通用性