为什么Java Collections不能通用删除方法?
为什么Collection.remove(Object o)不通用?
好像Collection <E>
可能有boolean remove(E o);
然后,当我们不小心尝试从Set <String>中删除(例如)Set <String>`而不是每个单独的String时,这将是编译时错误,而不是以后的调试问题。
解决方案
因为如果类型参数是通配符,则不能使用通用的remove方法。
我似乎想起了用Map的get(Object)方法遇到这个问题。在这种情况下,get方法不是通用的,尽管应该合理地期望将其传递给与第一个type参数相同类型的对象。我意识到,如果我们要传递带有通配符作为第一个类型参数的Maps,那么如果该参数是通用的,则无法使用该方法从Map中获取元素。不能真正满足通配符参数,因为编译器无法保证类型正确。我推测add是泛型的原因是,我们应该在将其添加到集合之前保证类型正确。但是,删除对象时,如果类型不正确,则无论如何都不会匹配任何内容。如果参数是通配符,则该方法将根本无法使用,即使我们可能拥有一个可以保证该对象属于该集合的对象,因为我们在上一行中仅得到了对它的引用。
我可能没有很好地解释它,但是对我来说似乎很合逻辑。
Remove不是通用方法,因此使用非通用集合的现有代码仍将编译且仍具有相同的行为。
有关详细信息,请参见http://www.ibm.com/developerworks/java/library/j-jtp01255.html。
编辑:评论者问为什么add方法是通用的。 [...删除了我的解释...]第二位评论者回答了firebird84的问题比我好得多。
因为它将破坏现有的(Java5之前的)代码。例如。,
Set stringSet = new HashSet(); // do some stuff... Object o = "foobar"; stringSet.remove(o);
现在我们可能会说上述代码是错误的,但是假设o来自不同种类的对象(即,它包含字符串,数字,对象等)。我们想删除所有匹配项,这是合法的,因为remove会忽略非字符串,因为它们不相等。但是,如果将其设置为remove(String o),它将不再起作用。
我一直认为这是因为remove()没有理由关心我们提供给它的对象的类型。无论如何,检查该对象是否是Collection包含的对象之一很容易,因为它可以对任何对象调用equals()。必须检查add()的类型,以确保它仅包含该类型的对象。
Josh Bloch和Bill Pugh在Java Puzzlers IV:The
幻影参考威胁,克隆的攻击和复仇
转移。
乔什·布洛赫(Josh Bloch)说(6:41)他们试图使get方法泛化
地图,删除方法和其他方法,但"它根本不起作用"。
如果有太多合理的程序无法生成
我们只允许将集合的通用类型作为参数类型。
他给出的示例是Number的List和a的交集。
长s的
名单`。
除了其他答案之外,还有另一个原因导致该方法应接受谓词"对象"。考虑以下示例:
class Person { public String name; // override equals() } class Employee extends Person { public String company; // override equals() } class Developer extends Employee { public int yearsOfExperience; // override equals() } class Test { public static void main(String[] args) { Collection<? extends Person> people = new ArrayList<Employee>(); // ... // to remove the first employee with a specific name: people.remove(new Person(someName1)); // to remove the first developer that matches some criteria: people.remove(new Developer(someName2, someCompany, 10)); // to remove the first employee who is either // a developer or an employee of someCompany: people.remove(new Object() { public boolean equals(Object employee) { return employee instanceof Developer || ((Employee) employee).company.equals(someCompany); }}); } }
关键是要传递给"删除"方法的对象负责定义"等于"方法。这样,构建谓词变得非常简单。
remove()
(在Map中以及在Collection
中)不是通用的,因为我们应该能够将任何类型的对象传递给remove()
。删除的对象不必与传递给remove()
的对象具有相同的类型;它只要求它们是平等的。从remove()
的规范中,remove(o)
删除对象e
,使得(o == null?e == null:o.equals(e))
为true
。注意,没有什么要求o
和e
必须是同一类型。这是基于这样的事实,即equals()方法采用了"对象"作为参数,而不仅仅是对象的类型。
虽然,通常确实有很多类都定义了" equals()",以便其对象只能等于其自己类的对象,但事实并非总是如此。例如,List.equals()的规范说两个List对象都是相同的,并且都是相同的内容,即使它们都是List的不同实现,也都是相等的。因此,回到这个问题的示例中,有可能具有一个Map <ArrayList,Something>并且我可以使用一个LinkedList作为参数来调用remove(),它应该删除具有相同内容的列表。如果remove()
是通用的并且限制了它的参数类型,那将是不可能的。