He escuchado el argumento de que debe usar la interfaz más genérica disponible para que no esté vinculado a una implementación particular de esa interfaz. ¿Se aplica esta lógica a interfaces como java.util.Collection ?
Preferiría ver algo como lo siguiente:
List<Foo> getFoos()
o
Set<Foo> getFoos()
en vez de
Collection<Foo> getFoos()
En el último caso, no sé con qué tipo de conjunto de datos estoy tratando, mientras que en los primeros dos casos puedo hacer algunas suposiciones sobre el orden y la unicidad. ¿Tiene java.util.Collection una utilidad fuera de ser un padre lógico tanto para conjuntos como para listas?
Si se encuentra con un código que empleó Collection cuando realiza una revisión de código, ¿cómo determinaría si su uso está justificado y qué sugerencias haría para su reemplazo por una interfaz más específica?
Collection<List<?>>
? ¡Habla sobre codificar el horror!Respuestas:
Las abstracciones viven más que las implementaciones
En general, cuanto más abstracto sea su diseño, más tiempo es probable que siga siendo útil. Entonces, dado que Collection es más abstracto que sus subinterfaces, es más probable que un diseño de API basado en Collection siga siendo útil que uno basado en List.
Sin embargo, el principio general es utilizar la abstracción más apropiada . Entonces, si su colección debe admitir elementos ordenados, entonces ordene una Lista, si no debe haber duplicados, entonces ordene un Conjunto, y así sucesivamente.
Una nota sobre diseño genérico de interfaz
Dado que está interesado en utilizar la interfaz de la Colección con genéricos, puede ser útil lo siguiente. Java eficaz por Joshua Bloch recomienda el siguiente enfoque al diseñar una interfaz que dependerá de genéricos: Producers Extend, Consumers Super
Esto también se conoce como la regla PECS . Esencialmente, si las colecciones genéricas que producen datos se pasan a su clase, la firma debería verse así:
Por lo tanto, el tipo de entrada puede ser E o cualquier subclase de E (E se define como una superclase y una subclase de sí mismo en el lenguaje Java).
Por el contrario, una colección genérica que se pasa para consumir datos debe tener una firma como esta:
El método será tratar correctamente con cualquier superclase de E. En general, el uso de este enfoque hará que su interfaz de menos sorprendente a sus usuarios, ya que será capaz de pasar en
Collection<Number>
yCollection<Integer>
y hacer que se tratan correctamente.fuente
La
Collection
interfaz, y la forma más permisivaCollection<?>
, es excelente para los parámetros que acepta. Según el uso en la propia biblioteca de Java , es más común como tipo de parámetro que como tipo de retorno.Para los tipos de devolución, creo que su punto es válido: si se espera que las personas accedan a él, deben conocer el orden (en el sentido Big-O) de la operación que se realiza. Repetiría un
Collection
devuelto y lo agregaría a otra Colección, pero parecería un poco loco recurrircontains
a él, sin saber si es una operación O (1), O (log n) u O (n). Por supuesto, solo porque tenga unSet
no significa que es un hashset o un conjunto ordenado, pero en algún momento hará suposiciones de que la interfaz se ha implementado razonablemente (y luego deberá ir al plan B si su suposición se muestra que es incorrecto).Como menciona Tom, a veces debe devolver un
Collection
para mantener la encapsulación: no desea que se filtren los detalles de implementación, incluso si pudiera devolver algo más específico. O, en el caso que Tom mencionó, podría devolver el contenedor más específico, pero luego tendría que construirlo.fuente
Lo miraría desde el punto de vista completamente opuesto y preguntaría:
Es bastante fácil justificar esto. Utiliza la Lista cuando necesita alguna funcionalidad que no ofrece una Colección. Si no necesita esa funcionalidad adicional, ¿qué justificación tiene? (Y no compraré, "Prefiero verlo")
Hay muchos casos en los que usará una colección con fines de solo lectura, rellenándola de una vez, repitiéndola por completo: ¿alguna vez necesita indexar la cosa manualmente?
Para dar un ejemplo real. Digamos que realizo una consulta simple en una base de datos. (
SELECT id,name,rep FROM people WHERE name LIKE '%squared'
) Recojo los datos relevantes, relleno los objetos Person y los pongo en una PersonList)Entonces, ¿qué justificación tendría para esos métodos adicionales? (que de todos modos quedaría sin implementar en mi PersonList)
fuente