为什么 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" 商品更高或更低的位置的商品。
举个现实生活中的例子:你有一场选美比赛。如果你问其中一个女孩是否比她旁边的那个更漂亮,她会说是的。您不能仅通过询问他们就对它们进行排序。所以你需要其他人负责排序,比较器。
这样您就可以订购无法与其他商品进行比较的商品。
如其他答案中所述,如果指定了自定义 Comparator
,TreeSet
键可能不是 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 中,所以这一次不是问题。现在我们必须处理向后兼容性问题。
我正在使用 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" 商品更高或更低的位置的商品。
举个现实生活中的例子:你有一场选美比赛。如果你问其中一个女孩是否比她旁边的那个更漂亮,她会说是的。您不能仅通过询问他们就对它们进行排序。所以你需要其他人负责排序,比较器。
这样您就可以订购无法与其他商品进行比较的商品。
如其他答案中所述,如果指定了自定义 Comparator
,TreeSet
键可能不是 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 中,所以这一次不是问题。现在我们必须处理向后兼容性问题。