resueltos framework example español ejercicios ejemplos collection colecciones coleccion java api generics collections

framework - ¿Por qué las colecciones Java no eliminan los métodos genéricos?



ejercicios resueltos de colecciones en java (9)

Además de las otras respuestas, hay otra razón por la cual el método debería aceptar un Object , que es predicado. Considere la siguiente muestra:

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

El punto es que el objeto que se pasa al método remove es responsable de definir el método equals . La construcción de predicados se vuelve muy simple de esta manera.

¿Por qué Collection.remove (Object o) no es genérico?

Parece que Collection<E> podría tener boolean remove(E o);

Luego, cuando accidentalmente intente eliminar (por ejemplo) Set<String> lugar de cada String individual de una Collection<String> , sería un error de tiempo de compilación en lugar de un problema de depuración más adelante.


Eliminar no es un método genérico, por lo que el código existente que utiliza una colección no genérica se compilará y seguirá teniendo el mismo comportamiento.

Consulte http://www.ibm.com/developerworks/java/library/j-jtp01255.html para obtener más información.

Editar: un comentarista pregunta por qué el método add es genérico. [... eliminé mi explicación ...] El segundo comentarista respondió la pregunta de firebird84 mucho mejor que yo.


Josh Bloch y Bill Pugh se refieren a este tema en Java Puzzlers IV: The Phantom Reference Menace, Attack of the Clone, y Revenge of The Shift .

Josh Bloch dice (6:41) que intentaron generar el método get de Map, eliminar el método y otro, pero "simplemente no funcionó".

Hay demasiados programas razonables que no podrían ser generados si solo permite el tipo genérico de la colección como tipo de parámetro. El ejemplo dado por él es una intersección de una List de Number sy una List de Long s.


Otra razón es debido a las interfaces. Aquí hay un ejemplo para mostrarlo:

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 rompería el código existente (previo a Java5). p.ej,

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

Ahora puede decir que el código anterior es incorrecto, pero supongamos que o proviene de un conjunto heterogéneo de objetos (es decir, que contenía cadenas, números, objetos, etc.). Desea eliminar todas las coincidencias, lo que era legal porque eliminar solo ignoraría las cadenas porque no eran iguales. Pero si lo haces eliminar (String o), eso ya no funciona.


Porque si su parámetro de tipo es un comodín, no puede usar un método de eliminación genérico.

Me parece recordar que encontré esta pregunta con el método get (Objeto) de Map. El método get en este caso no es genérico, aunque debe esperar razonablemente que se le pase un objeto del mismo tipo que el primer parámetro de tipo. Me di cuenta de que si está pasando Maps con un comodín como primer parámetro de tipo, entonces no hay forma de obtener un elemento del Mapa con ese método, si ese argumento fue genérico. Los argumentos del comodín no se pueden cumplir realmente, porque el compilador no puede garantizar que el tipo sea correcto. Yo especulo que el motivo por el cual add es genérico es que se espera que garantices que el tipo es correcto antes de agregarlo a la colección. Sin embargo, al eliminar un objeto, si el tipo es incorrecto, no coincidirá con nada de todos modos. Si el argumento fuera un comodín, el método sería simplemente inutilizable, incluso si puede tener un objeto que puede GARANTIZAR pertenece a esa colección, porque acaba de obtener una referencia en la línea anterior ....

Probablemente no lo haya explicado muy bien, pero me parece lo suficientemente lógico.


Siempre pensé que esto era porque remove () no tiene ninguna razón para preocuparse por el tipo de objeto que le das. Sin embargo, es bastante fácil verificar si ese objeto es uno de los que contiene la Colección, ya que puede llamar a igual () sobre cualquier cosa. Es necesario verificar type en add () para asegurarse de que solo contenga objetos de ese tipo.


Supongamos que uno tiene una colección de Cat y algunas referencias a objetos de los tipos Animal , Cat , SiameseCat y Dog . Preguntar a la colección si contiene el objeto al que se hace referencia en Cat o SiameseCat parece razonable. Preguntar si contiene el objeto al que hace referencia el Animal puede parecer dudoso, pero aún así es perfectamente razonable. Después de todo, el objeto en cuestión podría ser un Cat y podría aparecer en la colección.

Además, incluso si el objeto pasa a ser algo diferente a un Cat , no hay problema para decir si aparece en la colección: simplemente responda "no, no es así". Una colección de "tipo de búsqueda" de algún tipo debería ser capaz de aceptar de manera significativa la referencia de cualquier supertipo y determinar si el objeto existe dentro de la colección. Si la referencia del objeto transferido es de un tipo no relacionado, no hay manera de que la colección pueda contenerlo, por lo que la consulta en cierto sentido no es significativa (siempre responderá "no"). No obstante, dado que no hay ninguna forma de restringir los parámetros a subtipos o supertipos, es más práctico simplemente aceptar cualquier tipo y responder "no" a cualquier objeto cuyo tipo no esté relacionado con el de la colección.


remove() (en Map así como en Collection ) no es genérico porque deberías poder pasar cualquier tipo de objeto para remove() . El objeto eliminado no tiene que ser del mismo tipo que el objeto que se transfiere para remove() ; solo requiere que sean iguales. A partir de la especificación de remove() , remove(o) elimina el objeto e tal que (o==null ? e==null : o.equals(e)) es true . Tenga en cuenta que no hay nada que requiera que o y e sean del mismo tipo. Esto se deduce del hecho de que el método equals() toma un Object como parámetro, no solo del mismo tipo que el objeto.

Aunque, puede ser comúnmente cierto que muchas clases tienen equals() definido para que sus objetos solo puedan ser iguales a los objetos de su propia clase, que ciertamente no siempre es el caso. Por ejemplo, la especificación para List.equals() dice que dos objetos List son iguales si ambos son List y tienen el mismo contenido, incluso si son implementaciones diferentes de List . Volviendo al ejemplo de esta pregunta, es posible tener un Map<ArrayList, Something> y para llamar a remove() con LinkedList como argumento, y debería eliminar la clave que es una lista con los mismos contenidos. . Esto no sería posible si remove() fuera genérico y restringiera su tipo de argumento.