¿Cuándo utilizar métodos genéricos y cuándo utilizar comodines?

Resuelto benz asked hace 11 años • 9 respuestas

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 usado 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 deberían utilizar comodines. Los comodines están diseñados para admitir subtipos flexibles, que es lo que intentamos expresar aquí.

¿No creemos que el comodín (Collection<? extends E> c);también admite algún tipo de polimorfismo? Entonces, ¿por qué el uso de métodos genéricos no se considera bueno en esto?

Siguiendo adelante, afirma,

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é quiere decir 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 utilizar comodines en absoluto:

class Collections {
    public static <T, S extends T> void copy(List<T> dest, List<S> src) {
    ...
}

¿El documento desaconseja 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 iluminar esta zona?

benz avatar Aug 12 '13 03:08 benz
Aceptado

Hay ciertos lugares donde los comodines y los parámetros de tipo hacen lo mismo. Pero también hay ciertos lugares donde debes usar parámetros de tipo.

  1. Si desea imponer alguna relación en los diferentes tipos de argumentos del método, no puede hacerlo con comodines, debe usar parámetros de tipo.

Tomando su método como ejemplo, supongamos que desea asegurarse de que la lista srcy destpasada al copy()método sea del mismo tipo parametrizado, puede hacerlo con parámetros de tipo como este:

public static <T extends Number> void copy(List<T> dest, List<T> src)

Aquí, se garantiza que ambos desty srctienen el mismo tipo parametrizado para List. Por lo tanto, es seguro copiar elementos de srca dest.

Pero, si continúas cambiando el método para usar comodines:

public static void copy(List<? extends Number> dest, List<? extends Number> src)

no funcionará como se esperaba. En el segundo caso, puedes pasar List<Integer>y List<Float>como desty src. Por lo tanto, mover elementos de srca ya destno sería seguro para tipos. Si no necesita ese tipo de relación, entonces puede no utilizar ningún parámetro de tipo.

Algunas otras diferencias entre el uso de comodines y parámetros de tipo son:

  • Si solo tiene un argumento de tipo parametrizado, puede utilizar un comodín, aunque el parámetro de tipo también funcionará.

  • Los parámetros de tipo admiten múltiples límites, los comodines no.

  • Los comodines admiten límites superiores e inferiores, los parámetros de tipo solo admiten límites superiores. Entonces, si desea definir un método que tome un Listtipo de Integero su superclase, puede hacer:

     public void print(List<? super Integer> list)  // OK
    

pero no puedes usar un parámetro de tipo:

     public <T super Integer> void print(List<T> list)  // Won't compile

Referencias:

  • Preguntas frecuentes sobre genéricos de Java de Angelika Langer
Rohit Jain avatar Aug 11 '2013 21:08 Rohit Jain

Considere el siguiente ejemplo de The Java Programming de James Gosling, cuarta edición, donde queremos fusionar 2 SinglyLinkQueue:

public static <T1, T2 extends T1> void merge(SinglyLinkQueue<T1> d, SinglyLinkQueue<T2> s){
    // merge s element into d
}

public static <T> void merge(SinglyLinkQueue<T> d, SinglyLinkQueue<? extends T> s){
        // merge s element into d
}

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 generalmente es más legible que el código con múltiples parámetros de tipo. Al decidir si necesita una variable de tipo, pregúntese si esa variable de tipo se usa para relacionar dos o más parámetros. o para relacionar un tipo de parámetro con el tipo de retorno. Si la respuesta es no, entonces 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.

chammu avatar Jul 19 '2015 00:07 chammu