事情要从一次面试说起, 面试官问了这么一个问题, 在 JDK 下面这个方法中:
public static <T extends Comparable<? super T>> void sort(List<T> list)
这里面 < T extends Comparable<? super T>> 有什么用?
美好的愿景
很明显, 从语义上来说, sort 方法要对一个 List 排序, 这个 List 中的元素类型为 T,sort 方法要求这个 T 类型必须是可比较的. 这个可比较的含义是说, 任意两个 T 类型的对象, 可以通过 compareTo 方法来确定大小. 理想情况下, 一个 T 类型实现了 Comparable 接口的话, 那么对于任意两个属于 T 类型的对象 x 和 y, 都可以通过 compareTo 方法来确定大小. 因此, 方法写成如下形式即可.
public static <T extends Comparable> void sort(List<T> list)
Comparable 接口的无奈
可是现实世界里, 并不是 T 类型实现了 Comparable 接口就可以和 T 类型比较, 而是 T 类型实现 Comparable<E > 之后, 可以和 E 比较. 尽管 T 实现 Comparable<E > 毫无道理可言, 但是在语法上是正确的.
所以对于下面这个类, 只能说代码写得烂, 作者职业素养低, 而编译器却无能为力, 令人扼腕叹息.
public class Bar implements Comparable<String>
Java 泛型系统出来背一次锅
为了将这样的类拒之门外, sort 方法写成下面这样岂不是很好? sort 需要的 T 类型, 是一个能和 T 类型比较的类型, 真是天衣无缝啊.
public static <T extends Comparable<T>> void sort(List<T> list)
但是有这样一种情况, 假设有个类型 S,S 实现了 Comparable<S>, 然后 T 继承了 S, 那么 T 就具备了和 S 比较的能力. 但是这时 T 能通过编译么? 答案是不能, 因为在编译器看来, T 只具备和 S 比较的能力, 不具备和 T 比较的能力. 尽管 T 是 S 的子类, 但是编译器不认为如果一个类型具备了和 S 比较的能力, 就具备了和 T 比较的能力. 听上去感觉有点不合理, 就好比经常有猎头问我:"好了, 我现在知道你会用 hadoop,spark 这些来搞大数据了, 那么请问你会 Java 么?"
这个就是 Java 泛型系统的一个坑特性 -- 不具备协变, 逆变的能力, 所以尽管 Integer 是 Number 的子类, List<Integer > 却不是 List<Number > 子类, 一个能比较 Number 的比较器也不能被当做一个 Integer 的比较器.
所以最终用 < T extends Comparable<? super T>> 对 T 进行了约束, 这样不管 T 的 Comparable 是自己实现的也好, 还是继承的也好, 都可以海纳百川有容乃大的被 sort 方法接受了.
美中不足
但是 < T extends Comparable<? super T>> 约束能力还是不够强, 因为它在语义上表示的是 "T 能够和 super T 的任何类型比较", 所以下面这个类还是可以编译通过的, 除了你的同事会对你的编程水平和职业素养产生质疑.
public class Bar extends RuntimeException implements Comparable<Exception>
就像 equals 和 hashcode 的代码契约一样, 如果编译器在语法层面完全无法提供检查, 只能靠程序员的职业素养来产生良好的代码, 真是令人太不开心了.
来源: http://www.jianshu.com/p/8acee5f85582