2010-06-08 5 views
14

¿Es para mantener la compatibilidad con versiones anteriores (no genéricas) de Collection? ¿O hay un detalle más sutil que me falta? Veo este patrón repetido en remove también (remove(Object o)), pero add está genericized como add(E e).¿Por qué tenemos contiene (Objeto o) en lugar de contiene (E e)?

+2

posible duplicado de [¿Por qué las colecciones Java no eliminan los métodos genéricos?] (Http://stackoverflow.com/questions/104799/why-arent-java-collections-remove-methods-generic) – newacct

Respuesta

10

toma Object porque el objeto que coincide no tiene que ser del mismo tipo que el objeto que pasa a ; solo requiere que sean iguales. A partir de la especificación de , contains(o) devuelve verdadero si hay un objeto e tal que (o==null ? e==null : o.equals(e)) es cierto.Tenga en cuenta que no hay nada que requiera que o y e sean del mismo tipo. Esto se desprende del hecho de que el método equals() toma como parámetro Object, no solo del mismo tipo que el objeto.

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

De hecho, el hecho de que toma cualquier objeto como un argumento permite un uso interesante donde se puede usarlo para comprobar la existencia de un objeto en la colección que satisface una determinada propiedad:

Collection<Integer> integers; 
boolean oddNumberExists = integers.contains(new Object() { 
    public boolean equals(Object e) { 
     Integer i = (Integer)e; 
     if (i % 2 != 0) return true; 
     else return false; 
    } 
}); 
+3

También esta firma de método 'contiene (Objeto o)' proporciona una pistola realmente bonita para dispararse convenientemente en un pie. Este método es realmente un escaparate de la limitación de Java Generics. –

4

es porque la función contains utiliza la función equals, y la función equals se define en la clase de objeto de base con una firma de equals(Object o) en lugar de equals(E e) (ya que no todas las clases son de carácter genérico). El mismo caso con la función remove - atraviesa la colección utilizando la función equals que toma un argumento de objeto.

Esto no explica directamente la decisión, sin embargo, ya que podría haber todavía se utiliza el tipo E y permitió que fuera lanzado automáticamente al tipo de objeto en la llamada a equals; pero me imagino que querían permitir que se llamara a la función en otros tipos de objetos. No hay nada malo en tener un Collection<Foo> c; y luego llamar c.contains(somethingOfTypeBar) - siempre devolverá falso, y por lo que elimina la necesidad de un molde para escribir Foo (que puede lanzar una excepción) o, para proteger de la excepción, una llamada typeof. Así que puede imaginarse si está iterando sobre algo con tipos mixtos y llamando al contains en cada uno de los elementos, puede simplemente usar la función contains en todos ellos en lugar de necesitar guardias.

En realidad es una reminiscencia de los idiomas más "nuevos" mecanografiadas débilmente, cuando se mira de esa manera ...

+0

Esto no tiene nada que ver con hacer con la razón real. Sería mucho mejor tener algún nivel de comprobación de tipo en 'contiene' y quizás' eliminar'. Hubiera sido mucho menos propenso a errores. –

+3

"siempre devolverá falso" no, no lo hará. Es perfectamente posible que una clase califique un objeto de otra clase. – newacct

5

respondida aquí.
Why aren't Java Collections remove methods generic?
En resumen, querían maximizar la compatibilidad hacia atrás, ya que las colecciones se han introducido mucho antes de que los genéricos.

Y para añadir de mí: el video se está refiriendo vale la pena ver.
http://www.youtube.com/watch?v=wDN_EYUvUq0

actualización
Para aclarar, el hombre que dice que (en el video) era una de las personas que actualizaron mapas y colecciones de Java para utilizar los genéricos. Si él no sabe, entonces quién.

0

porque de lo contrario podría haber solamente puede comparar con la coincidencia exacta de tipo de parámetro, específicamente colecciones comodín habrían dejado de funcionar, por ejemplo,

class Base 
{ 
} 

class Derived 
    extends Base 
{ 
} 

Collection< ? extends Base > c = ...; 

Derived d = ...; 

Base base_ref = d; 

c.contains(d); // Would have produced compile error 

c.contains(base_ref); // Would have produced compile error 

EDITAR
Para los escépticos que piensan que no es una de las razones, aquí hay una lista de arreglo modificado con un estarían generified método contains

class MyCollection<E> extends ArrayList<E> 
{ 
    public boolean myContains(E e) 
    { 
     return false; 
    } 
} 

MyCollecttion< ? extends Base > c2 = ...; 

c2.myContains(d); // does not compile 
c2.myContains(base_ref); // does not compile 

Básicamente contains(Object o) es un truco para hacer este caso de uso muy común para trabajar con Java Generics.

+0

¿Cuidar para explicar el voto a la baja? –

+0

En el escenario normal, nadie querría lanzar una colección a 'Colección 'durante la manipulación del elemento. Si este es el caso, el método 'add (E e)' también habría sido un error. – Jai

+0

@Jai. Aquí hay un escenario 'void myMethod (Colección coll)'. Si dentro necesita ver si hay alguna versión de 'Foo' en esta recopilación, alcanzaría la limitación que se describe en esta respuesta. –

0

"¿esa cesta de manzanas contiene esta naranja?"

claramente no se puede dar una respuesta VERDADERA. pero eso aún deja demasiadas posibilidades:

  1. la respuesta es FALSA.
  2. la pregunta no está bien formada, no debe pasar la compilación.

la colección api eligió la primera. pero la segunda opción también tendría perfecto sentido. una pregunta como esa es una pregunta de mierda 99,99% de las veces, ¡así que no preguntes!

+0

La pregunta no debe considerarse mal formada, por ejemplo, si tiene dos cestas que contienen frutas, y quiere saber si alguna fruta en una canasta coincide con una fruta en la otra. Si uno supiera que una canasta solo contenía manzanas y una solo contenía naranjas, podría tener sentido cortocircuitar la búsqueda de una coincidencia, pero supongamos que se sabe que una canasta solo contiene manzanas y la otra contiene frutas mezcladas. Pedir la canasta de manzanas sobre cada fruta en la otra canasta sin validar su tipo primero sería más fácil que consultar cada fruta antes de preguntar si es una manzana. – supercat

+0

en mi ejemplo, se sabe que el parámetro es naranja, por lo tanto, la pregunta suena ridícula. en su ejemplo, la pregunta sería "¿esa cesta de manzanas contiene esta fruta?", eso es legítimo.el caso de uso es más raro. Podemos rediseñar la API para permitir su pregunta mientras prohibimos mi pregunta, al restringir el tipo de parámetro a super tipos de Apple. – irreputable

+0

No hay ningún mecanismo en .Net a través del cual un método pueda restringir un parámetro, genérico o de otro tipo, para que sea un supertipo de algún otro tipo, y tampoco creo que Java tenga dicho mecanismo. Tal cosa tendería a violar el Principio de Sustitución de Liskov, ya que un método que es utilizable con 'Fruta' debería ser utilizable con' Naranja'. Hay formas en .Net a través de las cuales uno podría usar etiquetas 'Obsoletas' para disparar chillidos de compilación en algunos casos tontos particularmente identificables, pero creo que eso sería más confuso que útil. – supercat

Cuestiones relacionadas