2012-12-15 54 views
13

如果我创建一个没有实现可比,并尝试使用它作为一个TreeSet的任意类,它在运行时抛出一个异常物体插入时:使用非Comparable类创建TreeSet:为什么运行时异常而不是编译时错误?

public class Foo { 
} 

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

我不是Java专家,但是有些事情似乎是以我没想到的方式动态输入的(ala Python)。 TreeSet的实现没有办法指定它的泛型类型参数必须实现Comparable,以便在编译时捕获它?非泛型函数可以将接口作为参数;与泛型相同吗?

回答

21

TreeSet以这种方式实现,因为您可以alternatively provide a Comparator,在这种情况下元素不需要是Comparable。在不将实现分成多个类的情况下支持这两种行为的唯一方法是包含运行时检查 - 这仅仅是该类作者的设计决定。

公开构造函数TreeSet而不是公共构造函数的工厂方法将会是一种使用更严格的泛型类型约束来维持编译时检查的方法,但是这将会与来自核心集合API暴露公有无参数约定并为其实现类复制构造函数。正如你在评论中提到的那样,Guava以其收藏品进入工厂路线,而恕我直言则更适合它。

+2

谢谢 - 这很有道理。实际上,我注意到Google的Guava库有一个[TreeMultiSet](http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/TreeMultiset.html#create(java.util .Comparator))类,它的设计就像你所描述的那样 - 有两个工厂,其中一个需要泛型扩展Comparable,另一个需要一个适当类型的Comparator。我想他们决定修正设计“bug”,以便为编译时间带来更多检查。 – uscjeremy

+2

是的,特别麻烦的是'TreeSet'不能失败,直到你真的把一个不起作用的元素放到了一个地方,这要归功于类型删除。幸运的是,它至少是“愚弄我一次”的一种失败,但仍然令人讨厌在编译时无法捕捉到它。 –

+3

我认为它实际上更糟 - 如果我有一堆'Foo'的子类,并且只有其中一些实现了Comparable,那么'add'不会抛出异常,直到最终插入一个非Comparables。谁知道什么时候会最终发生?这是一个令人沮丧的设计选择:我使用Java动态类型化语言,部分是为了摆脱混淆,直到每条代码路径都执行完毕才进行类型检查。 – uscjeremy

相关问题