Pergunta

Por que não é Collection.remove (Object o) genérico?

Parece que Collection<E> poderia ter boolean remove(E o);

Então, quando você acidentalmente tentar remover (por exemplo) Set<String> em vez de cada corda individual de um Collection<String>, seria um erro de tempo de compilação em vez de um problema de depuração mais tarde.

Foi útil?

Solução

Josh Bloch e Bill Pugh referem-se a esta questão em Java Puzzlers IV: O Fantasma Referência Menace, Attack of the Clone, e Revenge of The Mudança .

Josh Bloch diz (06:41) que eles tentaram generify o método get de Mapa, método remove e alguns outros, mas "simplesmente não funcionou".

Existem muitos programas razoáveis ??que não poderia ser generified se você só permitir que o tipo genérico da coleção como tipo de parâmetro. O exemplo dado por ele é uma interseção de um List de Numbers e uma List de Longs.

Outras dicas

remove() (em Map, bem como em Collection) não é genérico, porque você deve ser capaz de passar em qualquer tipo de objeto para remove(). O objeto removido não tem que ser do mesmo tipo que o objeto que você passa para remove(); ele só exige que eles sejam iguais. A partir da especificação de remove(), remove(o) remove o objecto e tal que (o==null ? e==null : o.equals(e)) é true. Note-se que não há nada que exige o e e ser do mesmo tipo. Isso decorre do fato de que o método equals() leva em uma Object como parâmetro, não apenas o mesmo tipo do objeto.

Embora, pode ser comumente verdade que muitas classes têm equals() definido para que seus objetos só pode ser igual a objetos de sua própria classe, que certamente não é sempre o caso. Por exemplo, a especificação para List.equals() diz que dois objetos de lista são iguais se eles são ambas as listas e têm o mesmo conteúdo, mesmo que sejam diferentes implementações de List. Então, voltando ao exemplo nesta questão, é possível ter uma Map<ArrayList, Something> e para mim para chamar remove() com um LinkedList como argumento, e deve remover a chave que é uma lista com o mesmo conteúdo. Isto não seria possível se remove() foram genéricos e restringiu a sua tipo de argumento.

Porque se o seu parâmetro de tipo é um curinga, você não pode usar um método de remoção genérico.

Eu me lembro de correr para essa pergunta com o método get (Object) do Mapa. O método get neste caso não é genérico, embora deva razoavelmente esperar para ser passado um objeto do mesmo tipo como o primeiro parâmetro tipo. Percebi que, se você está passando em torno Maps com um curinga como o primeiro parâmetro de tipo, então não há nenhuma maneira de obter um elemento fora do mapa com esse método, se esse argumento era genérico. argumentos curinga realmente não pode ser satisfeito, porque o compilador não pode garantir que o tipo está correto. I especulam que a razão add é genérico é que você é esperado para garantia de que o tipo está correto antes de adicionar à coleção. No entanto, ao remover um objeto, se o tipo está incorrecta, ele não corresponde a nada de qualquer maneira. Se o argumento fosse um wildcard o método seria simplesmente inutilizáveis, mesmo que você pode ter um objeto que você pode garantir pertence a essa coleção, porque você só tem uma referência a ele na linha anterior ....

Eu provavelmente não explicou muito bem, mas parece bastante lógico para mim.

Além das outras respostas, não há outra razão pela qual o método deve aceitar um Object, que é predicados. Considere o seguinte exemplo:

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);
        }});
    }
}

O ponto é que o objecto a ser passado para o método remove é responsável para a definição do método equals. Construção de predicados se torna muito simples desta forma.

Suponha que tem uma coleção de Cat, e algumas referências de objetos de tipos Animal, Cat, SiameseCat e Dog. Pedindo a coleção se ele contém o objeto referido pela referência Cat ou SiameseCat parece razoável. Perguntando se ele contém o objeto referido pela referência Animal pode parecer duvidoso, mas ainda é perfeitamente razoável. O objeto em questão poderia, afinal, ser um Cat, e pode aparecer na coleção.

Além disso, mesmo se o objeto passa a ser algo diferente de um Cat, não há problema em dizer se ele aparece na coleção - simplesmente responder "não, não". A coleção "pesquisa-style" de algum tipo deve ser capaz de aceitar significativamente referência de qualquer supertipo e determinar se o objeto existe dentro da coleção. Se a referência passou-in objeto é de um tipo não relacionado, não há nenhuma maneira a coleção poderia contê-lo, de modo que a consulta é em certo sentido não significativa (ele sempre responder "não"). No entanto, uma vez que não há nenhuma maneira de restringir parâmetros para ser subtipos ou supertipos, é mais prático simplesmente aceitar qualquer tipo e resposta "não" para qualquer objeto cujo tipo não está relacionada com a da coleção.

Sempre achei isso foi porque remove () não tem qualquer motivo para se importar que tipo de objeto você lhe dá. É fácil o suficiente, independente, para verificar se o objeto é um dos mais a coleção contém, uma vez que pode chamar equals () em qualquer coisa. É necessário verificar tipo de add () para garantir que ele contém apenas objetos desse tipo.

Remove não é um método genérico para que o código existente usando uma coleção não genérico ainda irá compilar e ainda ter o mesmo comportamento.

http://www.ibm.com/developerworks/ java / biblioteca / j-jtp01255.html para mais detalhes.

Edit: Um comentarista pergunta por que o método add é genérico. [... removido minha explicação ...] Em segundo lugar comentarista respondeu à pergunta de firebird84 muito melhor do que eu.

Outra razão é por causa de interfaces. Aqui está um exemplo para mostrar que:

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 */
}

Porque ele iria quebrar existente (pré-Java5) código. por exemplo.,

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

Agora você pode dizer o código acima é errado, mas supor que o veio de um conjunto heterogêneo de objetos (ou seja, continha cordas, número, objetos, etc.). Você deseja remover todas as partidas, o que foi legal porque remove iria simplesmente ignorar os não-cordas, porque eles não eram iguais. Mas se você fizer isso remover (String o), que já não funciona.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top