为什么 TreeSet 声明为 TreeSet<E> 而不是 TreeSet<E extends Comparable<E>>

Why is TreeSet declared TreeSet<E> instead of TreeSet<E extends Comparable<E>>

我正在使用 TreeSet 并在调用 TreeSet#add() 方法时发现了一个 ClassCastException

代码:

public class Testing {
    public static void main(String[] args) {
        TreeSet<Testing> ts = new TreeSet<>();
        ts.add(new Testing());
    }
}

输出:

Exception in thread "main" java.lang.ClassCastException: Testing cannot be cast to java.lang.Comparable
    at java.util.TreeMap.compare(TreeMap.java:1290)
    at java.util.TreeMap.put(TreeMap.java:538)
    at java.util.TreeSet.add(TreeSet.java:255)
    at Testing.main(Testing.java:13)

显然是因为 TreeSet 是一个 有序集合 并且它需要 Comparable 个对象来对它们进行排序,所以为什么不将其类型声明为

public class TreeSet<E extends Comparable<E>>

并在编译时进行检查而不是在运行时抛出异常?

A TreeSet 的元素不必实现 Comparable,因为您可以将 Comparator 传递给 TreeSet 的构造函数之一,以便对未实现 Comparable 的元素(或当您想要使用 Comparable 定义的自然顺序以外的顺序时,对实现 Comparable 的元素强加一个顺序)。

其实施方式,您可以订购无法自行决定是否应将其订购在比 "other" 商品更高或更低的位置的商品。

举个现实生活中的例子:你有一场选美比赛。如果你问其中一个女孩是否比她旁边的那个更漂亮,她会说是的。您不能仅通过询问他们就对它们进行排序。所以你需要其他人负责排序,比较器。

这样您就可以订购无法与其他商品进行比较的商品。

如其他答案中所述,如果指定了自定义 ComparatorTreeSet 键可能不是 Comparable。仍然可以为您的案例强制执行编译时检查。假设我们将默认构造函数设为私有并提供静态工厂方法:

public class TreeSet<E> {
    private TreeSet() {...}

    public static <E extend Comparable<? super E>> TreeSet<E> newSet() {
        return new TreeSet<>();
    }
}

这样你将被迫使用 TreeSet.newSet() 并且如果你将它分配给 TreeSet<Testing> 并且 Testing 不可比较,编译时类型检查将失败。为什么没有完成?因为泛型只出现在 Java 1.5 中,而 TreeSet 出现在 Java 1.2 中,所以这一次不是问题。现在我们必须处理向后兼容性问题。