因为? super t允许传入能比较t及其父类的comparator,既支持子类数组用父类比较器排序(如person[]用comparator),又保障类型安全与泛型协变。

下界通配符(? super T)在排序比较中的关键作用,是让 Comparator 能安全地基于 T 或其父类的公共属性 进行比较,同时保持类型灵活性和写入兼容性。
为什么排序方法参数常用 ? super T
Java 标准库中很多排序方法(如 Collections.sort()、Arrays.sort())的 Comparator 参数声明为 Comparator super T>,而不是 Comparator<t></t>。这不是随意设计,而是为了支持更宽泛的比较逻辑:
- 若传入
Comparator<animal></animal>,它能比较所有Animal子类(如Cat、Dog)实例——因为子类对象可安全赋值给父类引用; - 若限定为
Comparator<cat></cat>,就无法用于List<animal></animal>或List<object></object>,丧失复用性; -
? super Cat表示“接受 Cat 或 Cat 的任意父类的 Comparator”,意味着该比较器具备处理更通用类型的潜力。
Comparator 如何利用父类属性实现跨子类排序
当多个子类共享父类字段(例如 Animals 有 name 字段,Cats 和 Dogs 都继承它),一个针对父类的 Comparator 就能统一排序不同子类对象:
- 定义
Comparator<animals></animals>,按getName()排序; - 该比较器可直接用于
TreeSet<cats></cats>(构造时传入),因为TreeSet<cats>(new Comparator<animals>(){...})</animals></cats>是合法的——Comparator super Cats>允许传入Comparator<animals></animals>; - 插入
Cats实例时,TreeSet内部调用该Comparator的compare()方法,而Cats对象可被当作Animals使用,getName()可正常访问。
TreeSet 实例化时的典型写法
以知识库中的 Animals 和 Cats 为例:
-
TreeSet<cats> set = new TreeSet(Comparator.comparing(Cats::getName));</cats>→ 编译失败(Cats::getName返回String,但Cats没有getName()方法,除非显式继承或重写); - 正确方式是利用父类字段:
TreeSet<cats> set = new TreeSet((c1, c2) -> c1.getName().compareTo(c2.getName()));</cats>—— 此处c1、c2是Cats,但getName()来自Animals; - 更通用写法:
Comparator<animals> byName = Comparator.comparing(Animals::getName);</animals>,然后new TreeSet<cats>(byName)</cats>成立,因Comparator<animals></animals>符合Comparator super Cats>约束。
不写死类型,才能真正复用比较逻辑
下界通配符的本质价值,在于解耦“被排序的数据类型”和“执行比较的依据类型”:
- 数据是
Cats,但比较依据是父类Animals的name; - 同一份
Comparator<animals></animals>也能用于TreeSet<dogs></dogs>、TreeSet<animals></animals>,甚至List<object></object>(只要元素实际是Animals子类); - 没有
? super T,这种跨层级复用必须靠泛型方法或类型转换,既不安全也不简洁。










