为什么不';Java集合是否删除泛型方法?

为什么不';Java集合是否删除泛型方法?,java,api,generics,collections,Java,Api,Generics,Collections,为什么不是通用的 似乎集合可以有布尔删除(EO) 然后,当您不小心尝试从集合中删除(例如)Set而不是每个字符串时,这将是一个编译时错误,而不是以后的调试问题。因为如果您的类型参数是通配符,则不能使用通用的删除方法 我似乎记得在使用Map的get(Object)方法时遇到过这个问题。在这种情况下,get方法不是泛型的,尽管它理应被传递给与第一个类型参数相同类型的对象。我意识到,如果你用一个通配符作为第一个类型参数来传递映射,那么如果这个参数是泛型的,那么就没有办法用这个方法从映射中获取元素。不能

为什么不是通用的

似乎
集合
可以有
布尔删除(EO)


然后,当您不小心尝试从
集合中删除(例如)
Set
而不是每个字符串时,这将是一个编译时错误,而不是以后的调试问题。

因为如果您的类型参数是通配符,则不能使用通用的删除方法

我似乎记得在使用Map的get(Object)方法时遇到过这个问题。在这种情况下,get方法不是泛型的,尽管它理应被传递给与第一个类型参数相同类型的对象。我意识到,如果你用一个通配符作为第一个类型参数来传递映射,那么如果这个参数是泛型的,那么就没有办法用这个方法从映射中获取元素。不能真正满足通配符参数,因为编译器不能保证类型正确。我推测add是泛型的原因是,在将其添加到集合之前,您需要保证类型是正确的。但是,当删除对象时,如果类型不正确,那么它无论如何都不会匹配任何内容。如果参数是通配符,则该方法将无法使用,即使您可能有一个可以保证属于该集合的对象,因为您在前一行中刚刚获得了对该对象的引用


我可能没有很好地解释它,但对我来说它似乎足够合乎逻辑。

Remove不是一个泛型方法,因此使用非泛型集合的现有代码仍然可以编译,并且仍然具有相同的行为

有关详细信息,请参阅


编辑:评论员询问为什么add方法是泛型的。[…删除了我的解释…]第二位评论员回答firebird84的问题比我好得多。

因为它会破坏现有(Java5之前)代码。e、 g

Set stringSet = new HashSet();
// do some stuff...
Object o = "foobar";
stringSet.remove(o);

现在您可能会说上面的代码是错误的,但是假设o来自一组异构对象(即,它包含字符串、数字、对象等)。您希望删除所有匹配项,这是合法的,因为remove只会忽略非字符串,因为它们不相等。但如果您将其删除(字符串o),则该操作将不再有效

我一直认为这是因为remove()没有理由关心你给它什么类型的对象。不管怎样,检查该对象是否是集合包含的对象之一非常容易,因为它可以对任何对象调用equals()。有必要检查add()上的类型,以确保它只包含该类型的对象。

Josh Bloch和Bill Pugh在中提到了这个问题

Josh Bloch说(6:41),他们试图泛化get方法 地图,删除方法和其他一些,但“它根本没有工作”

有太多合理的程序,如果 您只允许集合的泛型类型作为参数类型。 他给出的例子是
编号的
列表

List
of
Long
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);
        }});
    }
}
班级人员{
公共字符串名称;
//覆盖等于()
}
类雇员扩展个人{
上市公司;
//覆盖等于()
}
类开发人员扩展了Employee{
公众经验;
//覆盖等于()
}
课堂测试{
公共静态void main(字符串[]args){
集合
remove()
(在
Map
以及
Collection
中)不是通用的,因为您应该能够将任何类型的对象传入
remove()
。移除的对象不必与传入
remove()的对象类型相同
;它只要求它们相等。从
remove()
的规范中,
remove(o)
删除对象
e
,以便
(o==null?e==null:o.equals(e))
true
。请注意,没有任何东西要求
o
e
为同一类型。这是因为
equals()
方法将
对象
作为参数,而不仅仅是对象的同一类型


尽管许多类通常都定义了
equals()
,以便其对象只能等于其自己类的对象,但情况并非总是如此。例如,
List.equals()的规范
说,如果两个列表对象都是列表并且具有相同的内容,那么它们是相等的,即使它们是
List
的不同实现。因此回到这个问题中的示例,有一个
Map
并调用
remove()
使用
LinkedList
作为参数,它应该删除具有相同内容的列表的键。如果
remove()
是泛型的,并且限制了它的参数类型。

假设有一个
Cat
的集合,以及一些类型为
Animal
Cat
siamescat
Dog
的对象引用。询问集合是否包含
Cat
siamescat
引用的对象引用似乎是合理的。询问它是否包含
动物
引用所引用的对象似乎是不可靠的,但它仍然是完全合理的。所讨论的对象可能毕竟是
,并且可能出现在集合中

此外,即使对象恰好不是一个
Cat
,也可以确定它是否出现在集合中——只需回答“不,不是”。某种类型的“查找样式”集合应该能够有意义地接受任何superty的引用
public interface A {}

public interface B {}

public class MyClass implements A, B {}

public static void main(String[] args) {
   Collection<A> collection = new ArrayList<>();
   MyClass item = new MyClass();
   collection.add(item);  // works fine
   B b = item; // valid
   collection.remove(b); /* It works because the remove method accepts an Object. If it was generic, this would not work */
}