Estoy leyendo sobre métodos genéricos de OracleDocGenericMethod . Estoy bastante confundido acerca de la comparación cuando dice cuándo usar comodines y cuándo usar métodos genéricos. Citando del documento.
interface Collection<E> { public boolean containsAll(Collection<?> c); public boolean addAll(Collection<? extends E> c); }
Podríamos haber utilizado métodos genéricos aquí en su lugar:
interface Collection<E> { public <T> boolean containsAll(Collection<T> c); public <T extends E> boolean addAll(Collection<T> c); // Hey, type variables can have bounds too! }
[…] Esto nos dice que el argumento de tipo se está utilizando para polimorfismo; su único efecto es permitir el uso de una variedad de tipos de argumentos reales en diferentes sitios de invocación. Si ese es el caso, se deben usar comodines. Los comodines están diseñados para admitir subtipos flexibles, que es lo que estamos tratando de expresar aquí.
¿No creemos que el comodín como (Collection<? extends E> c);
también apoya una especie de polimorfismo? Entonces, ¿por qué el uso de métodos genéricos no se considera bueno en esto?
Continuando adelante, dice,
Los métodos genéricos permiten utilizar parámetros de tipo para expresar dependencias entre los tipos de uno o más argumentos de un método y / o su tipo de retorno. Si no existe tal dependencia, no se debe utilizar un método genérico.
¿Qué significa esto?
Han presentado el ejemplo
class Collections { public static <T> void copy(List<T> dest, List<? extends T> src) { ... }
[…]
Podríamos haber escrito la firma para este método de otra manera, sin usar comodines en absoluto:
class Collections { public static <T, S extends T> void copy(List<T> dest, List<S> src) { ... }
¿El documento desalienta la segunda declaración y promueve el uso de la primera sintaxis? ¿Cuál es la diferencia entre la primera y la segunda declaración? ¿Ambos parecen estar haciendo lo mismo?
¿Alguien puede poner luz en esta área?
?
en absoluto. Puede reescribirlo como `public static <T1 extiende Número, T2 extiende Número> copia vacía (Lista <T1> dest, Lista <T2> src) y en este caso se vuelve obvio lo que está pasando.List
parámetro de tipo de uso.List<T super Integer>
no es válido y no se compilará.<T extends X & Y>
-> límites múltiples.Considere el siguiente ejemplo de The Java Programming por James Gosling 4ta edición a continuación, donde queremos fusionar 2 SinglyLinkQueue:
Ambos métodos anteriores tienen la misma funcionalidad. Entonces, ¿cuál es preferible? La respuesta es la segunda. En palabras del propio autor:
"La regla general es usar comodines cuando sea posible porque el código con comodines es generalmente más legible que el código con varios parámetros de tipo. Cuando decida si necesita una variable de tipo, pregúntese si esa variable de tipo se usa para relacionar dos o más parámetros. o relacionar un tipo de parámetro con el tipo de retorno. Si la respuesta es no, un comodín debería ser suficiente ".
Nota: En el libro, solo se proporciona el segundo método y el nombre del parámetro de tipo es S en lugar de 'T'. El primer método no está en el libro.
fuente
En su primera pregunta: Significa que si existe una relación entre el tipo de parámetro y el tipo de retorno del método, utilice un archivo.
Por ejemplo:
Aquí estás extrayendo algunos de los T siguiendo ciertos criterios. Si T es,
Long
sus métodos volveránLong
yCollection<Long>
; el tipo de retorno real depende del tipo de parámetro, por lo que es útil y aconsejable utilizar tipos genéricos.Cuando este no es el caso, puede utilizar tipos de comodines:
En estos dos ejemplos, cualquiera que sea el tipo de elementos de las colecciones, los tipos de devolución serán
int
yboolean
.En tus ejemplos:
esas dos funciones devolverán un booleano cualquiera sea el tipo de elementos de las colecciones. En el segundo caso, se limita a instancias de una subclase de E.
Segunda pregunta:
Este primer código le permite pasar un heterogéneo
List<? extends T> src
como parámetro. Esta lista puede contener múltiples elementos de diferentes clases siempre que todos extiendan la clase base T.si tuvieras:
y
Podrías hacerlo
Por otra parte
Restringir
List<S> src
para ser de una clase particular S que es una subclase de T. La lista sólo puede contener elementos de una clase (en este caso S) y ninguna otra clase, incluso si implementan T también. No podría usar mi ejemplo anterior, pero podría hacer:fuente
List<? extends Fruit> basket = new ArrayList<? extends Fruit>();
No es una sintaxis válida. Tienes que crear una instancia de ArrayList sin límites.ArrayList(Collection<? extends E> c)
. ¿Puede explicarme por qué ha dicho eso?El método comodín también es genérico; puede llamarlo con algún rango de tipos.
La
<T>
sintaxis define un nombre de variable de tipo. Si una variable de tipo tiene algún uso (por ejemplo, en la implementación de un método o como una restricción para otro tipo), entonces tiene sentido nombrarla; de lo contrario, podría usarla?
como variable anónima. Entonces, parece solo un atajo.Además, la
?
sintaxis no se puede evitar cuando declaras un campo:fuente
Intentaré responder a su pregunta, una por una.
No. La razón es que el comodín acotado no tiene un tipo de parámetro definido. Es un desconocido. Todo lo que "sabe" es que la "contención" es de un tipo
E
(cualquiera que sea su definición). Por lo tanto, no puede verificar y justificar si el valor proporcionado coincide con el tipo acotado.Entonces, no es sensato tener comportamientos polimórficos en comodines.
La primera opción es mejor en este caso, ya
T
que siempre está limitada ysource
definitivamente tendrá valores (de incógnitas) que subclasesT
.Entonces, suponga que desea copiar toda la lista de números, la primera opción será
src
, Esencialmente, puede aceptarList<Double>
,List<Float>
, etc., ya que hay un límite superior para el tipo parametrizado encuentra endest
.La segunda opción te obligará a vincular
S
todos los tipos que quieras copiar, asíComo
S
es un tipo parametrizado que necesita vinculante.Espero que esto ayude.
fuente
<S extends T>
establece queS
es un tipo parametrizado que es una subclase deT
, por lo que requiere un tipo parametrizado (sin comodines) que sea una subclase deT
.Otra diferencia que no se menciona aquí.
Pero lo siguiente resultará en un error de tiempo de compilación.
fuente
Por lo que tengo entendido, solo hay un caso de uso en el que el comodín es estrictamente necesario (es decir, puede expresar algo que no puede expresar utilizando parámetros de tipo explícitos). Aquí es cuando necesita especificar un límite inferior.
Aparte de eso, sin embargo, los comodines sirven para escribir código más conciso, como se describe en las siguientes declaraciones en el documento que menciona:
fuente
Principalmente -> Los comodines hacen cumplir los genéricos en el nivel de parámetro / argumento de un método no genérico. Nota. También se puede realizar en genericMethod de forma predeterminada, pero aquí en lugar de? podemos usar la T misma.
paquete de genéricos;
SO wildcard tiene sus casos de uso específicos como este.
fuente