为什么TreeSet声明TreeSet <e>而不是TreeSet <e extends =“”可比较<e =“”>&gt;

时间:2016-03-03 11:18:12

标签: java generics treeset

我正在使用TreeSet并在调用ClassCastException方法时找到了TreeSet#add()

代码:

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>>

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

3 个答案:

答案 0 :(得分:7)

TreeSet个元素不必实施Comparable,因为您可以将Comparator传递给TreeSet&#39之一; s构造函数,以便为不要实现Comparable的元素强加一个排序(或者当你想要使用除Comparable定义的自然排序之外的其他排序时执行Comparable的元素。 {1}})。

答案 1 :(得分:1)

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

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中,而{1}}出现在Java 1.2中,所以这次不是问题。现在我们必须处理向后兼容性。

答案 2 :(得分:0)

实施方式,您可以订购无法自行决定的商品,如果他们的订购地点比“其他”商品更高或更低。

以现实生活为例:你有一场选美比赛。如果你问其中一个女孩她是否比她旁边的女孩更漂亮,她会说是的。你不能仅仅通过问他们就把它们放在一个订单中。因此,您需要其他人来负责订购,比较器。

这允许您订购无法与其他项目进行比较的项目。