使用非Comparable类创建TreeSet:为什么是运行时异常,而不是编译时错误?

usc*_*emy 15 java generics treeset

如果我创建一个没有实现Comparable的任意类,并尝试将其用作树集,则在插入对象时会在运行时抛出异常:

public class Foo {
}

public TreeSet<Foo> fooSet = new TreeSet<Foo>();
fooSet.add(new Foo()); // Throws a ClassCastException exception here: Foo is not comparable
Run Code Online (Sandbox Code Playgroud)

我不是Java专家,但有些事情看起来像我没想到的那样动态地输入(ala Python).TreeSet的实现是否无法指定其泛型类型参数必须实现Comparable以便可以在编译时捕获它?非泛型函数可以将接口作为参数; 仿制药是不可能的?

Pau*_*ora 30

TreeSet以这种方式实现,因为你可以选择提供一个Comparator,在这种情况下元素不需要Comparable.在不将实现分成多个类的情况下支持这两种行为的唯一方法是包括运行时检查 - 这只是该类作者的设计决策.

公开TreeSet构造函数公开工厂方法将是一种使用更严格的泛型类型约束来维护编译时间检查的方法,但这将是核心集合API公开公开的非参数和复制构造函数的惯例.实施课程.正如你在评论中指出的那样,Guava带着它的收藏品走上了工厂路线,恕我直言就更好了.

  • 我认为它实际上更糟 - 如果我有一堆Foo`的`子类,只有其中的一些实现可比,`add`不会抛出异常,直到最后插入非比较公司之一.谁知道什么时候最终会发生?这是一个令人沮丧的设计选择:我用Java在动态类型语言部分,从直到每个代码路径已执行没有类型检查的混乱逃脱! (3认同)
  • 谢谢 - 这很有道理.事实上,我注意到Google的Guava库有一个[TreeMultiSet](http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/TreeMultiset.html#create(java.util) .Comparator))正如你所描述的那样设计的类 - 有两个工厂,其中一个需要通用扩展Comparable,其中一个需要一个适当类型的Comparator.我猜他们决定修复设计"bug",以便为编译时间带来更多的检查. (2认同)
  • 是的,特别麻烦的是,由于类型擦除,`TreeSet`不会失败,直到你实际上"放置"一个不起作用的元素.幸运的是,它至少是一种"愚弄我一次"的失败,但仍然很烦人,不要在编译时捕获它. (2认同)