
Comparator或函数式接口实现)是指行为类似于函数的对象。
qsort函数)。但函数指针有几个缺点:
operator() (C++),或者实现了某个特定接口(Java中的Comparator,Runnable)。这个类的实例就是一个对象,但可以像函数一样被调用。
2. 泛型 由来:解决类型安全问题 在泛型出现之前(Java 1.4及以前,C通过
void*),我们使用Object类型来构建通用的数据结构(如ArrayList可以存任何东西)。
Object时,必须向下转型为具体类型,代码臃肿。
ClassCastException。
ArrayList<String> 中的 String 就是类型参数。
在数据结构中的应用 几乎所有的现代数据结构都是泛型的。
IntList、StringList、StudentList 分别写代码。写一个 List<T> 就够了。
List<String>,向里面添加Integer对象会在编译时报错。
3. 包装类 由来:弥补“一切皆对象”的缺口 在纯粹的面向对象语言(如Java)中,泛型只能使用引用类型(对象),不能使用基本类型(如
int、double、char)。然而,int和double在数学计算和性能上又必不可少。
int -> Integer
double -> Double
char -> Character
自动装箱/拆箱 为了使用方便,现代语言(Java 5+)支持自动装箱(Autoboxing)和自动拆箱(Unboxing)。
int i = 5; Integer obj = i; (自动将基本类型转为对象)
Integer obj = 5; int i = obj; (自动将对象转为基本类型)
在数据结构中的应用 由于泛型无法直接接受基本类型,包装类成为了桥梁。
PriorityQueue<T> 中,通过传入一个 Comparator<T> 函数对象,你可以动态地改变“优先级”的定义(是最小值优先还是最大值优先)。
通配符使用问号 ? 来表示未知类型。它主要出现在变量声明、方法参数或返回值中,用来解决泛型类型不一致的问题。
场景:你想写一个方法,打印任意类型的列表。
java
// 假设没有通配符,你可能会这么写:
public static void printList(List<Object> list) {
for (Object obj : list) {
System.out.println(obj);
}
}问题:这个方法只能接受 List<Object>,而无法接受 List<String> 或 List<Integer>。
因为 List<String> 不是 List<Object> 的子类,即使 String 是 Object 的子类。这就是所谓的泛型不变性。
解决方案:使用通配符 List<?>。
public static void printList(List<?> list) { // 可以接收任何类型的 List
for (Object obj : list) { // 因为类型未知,所以只能当作 Object 处理
System.out.println(obj);
}
}想象你是一个仓库管理员,面前有几个盒子:
List<Apple>:一个只装苹果的盒子。
List<Fruit>:一个可以装各种水果的盒子。
List<Object>:一个什么都能装的万能盒子。
通配符是什么?
List<?>:
这就像让你去接收一个密封的盒子。你知道里面肯定是水果(对象),但不知道具体是什么。
list.size()),或者把盒子里的东西当作普通物品拿出来看看(赋值给 Object),因为你不知道具体是什么水果,所以你不能往里面放东西(除了 null)。
<? extends T>定义:表示类型是 T 或 T 的子类。
形象解释:只读的展示柜
你有一个 List<? extends Fruit>,这相当于一个展示水果的玻璃柜。
Apple),可能是香蕉(Banana),但肯定是水果。
Fruit)看待。
<? super T>定义:表示类型是 T 或 T 的超类(父类)。
形象解释:可写入的收集箱
你有一个 List<? super Apple>,这相当于一个收集苹果的箱子。
List<Apple>,也可能是 List<Fruit>,甚至是 List<Object>。具体是什么不确定,但肯定能装苹果。
Apple)肯定没错。
扔青苹果(GreenApple,Apple的子类)也行。
Box<T> 就像一条专门生产某类玩具的生产线。如果你启动了 Box<Car> 生产线,这条线上的机械臂(实例方法)都知道自己抓取的是 Car。
<U> Box<U> createBox(U item) 就像工厂里的万能启动钥匙或者一条灵活的小型工作台。
你给它一块木头(Wood),它就启动一个专门生产木头盒子的生产线(Box<Wood>)。
你给它一块铁(Iron),它就启动一个专门生产铁盒子的生产线(Box<Iron>)。
这个启动方法不依赖于任何一条具体的生产线,它自己就能根据原料决定启动哪条线。
Collections 工具类:Collections.emptyList() 返回一个空的 List<T>。
总结一下
?):解决的是“我要用一段代码处理多种泛型类型”的问题。
? extends T:只能读,不能写(除非 null)。适合生产者(读取数据)。
? super T:只能写,读只能当 Object。适合消费者(写入数据)。
Producer Extends, Consumer Super(生产者使用 Extends,消费者使用 Super)。
结语:如果对你有帮助,请点赞,关注,收藏,你的支持就是我最大的鼓励!