2012-06-08 15 views
33

yo soy un novato en genérico y mi pregunta es: ¿qué diferencia entre dos funciones:diferencia entre el tipo genérico y comodín tipo

función 1:

public static <E> void funct1 (List<E> list1) { 

} 

función 2:

public static void funct2(List<?> list) { 

} 

Gracias.

+1

Si String se refiere a 'java.lang.String', creo que funct2 es bastante redundante en las expresiones de comodines, ya que' java.lang.String' es la clase final y no se puede extender. – nhahtdh

+1

Esto es extraño porque técnicamente no se puede extender String ya que es una clase final. –

+0

Sin embargo, no hay ninguna restricción en el uso de la función 2: ya que permite subclases de Cadena (aunque imposible) o la clase de Cadena en sí ... – ria

Respuesta

29

La primera firma dice: list1 es una lista de Es.

La segunda firma dice: list es una lista de instancias de algún tipo, pero no conocemos el tipo.

La diferencia se hace evidente cuando tratamos de cambiar el método por lo que toma un segundo argumento, que debe ser añadido a la lista dentro del método:

import java.util.List; 

public class Experiment { 
    public static <E> void funct1(final List<E> list1, final E something) { 
     list1.add(something); 
    } 

    public static void funct2(final List<?> list, final Object something) { 
     list.add(something); // does not compile 
    } 
} 

El primero se trabaja muy bien. Y no puedes cambiar el segundo argumento en algo que compile realmente.

En realidad, yo acaba de encontrar una demostración aún más agradable de la diferencia:

public class Experiment { 
    public static <E> void funct1(final List<E> list) { 
     list.add(list.get(0)); 
    } 

    public static void funct2(final List<?> list) { 
     list.add(list.get(0)); // !!!!!!!!!!!!!! won't compile !!!!!!!!! 
    } 
} 

Uno puede ser que ¿por qué necesitamos <?> cuando sólo restringe lo que podemos hacer con ella (como @Babu_Reddy_H hizo en los comentarios) . Veo los siguientes beneficios de la versión de comodín:

  • La persona que llama tiene que saber menos sobre el objeto que pasa Por ejemplo si tengo un mapa de Listas:. Map<String, List<?>> me puede pasar los valores de acuerdo a su función sin especificando el tipo de los elementos de la lista. Así que

  • Si entrego objetos con esta configuración, limito activamente lo que la gente sabe sobre estos objetos y lo que pueden hacer con él (siempre que se mantengan alejados de la transmisión insegura).

Estos dos tienen sentido cuando los combino: List<? extends T>. Por ejemplo, considere un método List<T> merge(List<? extends T>, List<? extends T>), que combina las dos listas de entrada con una nueva lista de resultados. Claro que podrías introducir dos parámetros de tipo más, pero ¿por qué querrías? Sería más de especificar cosas.

  • finalmente comodines puede tener límites más bajos, por lo que con las listas que puede hacer el trabajo add método, mientras que get no te da nada útil. Por supuesto, eso desencadena la siguiente pregunta: ¿por qué los genéricos no tienen límites más bajos?

Para una respuesta más en profundidad, véase: When to use generic methods and when to use wild-card? y http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#FAQ203

+0

Gracias, entendí – Fio

+4

También vale la pena señalar que no se trata solo de poner. Con una 'Lista ', puede escribir 'E obj = list.get (0)', pero con 'List ' solo puede escribir 'Object obj = list.get (0)'. – yshavit

+0

@Jens schauder. Ambos ? y E permite que solo los métodos de objeto invoquen en el objeto. Y uno puede agregar a la lista y no puede hacer lo mismo con la lista . Entonces, ¿por qué necesitamos la Lista ? –

1

La lista como parámetro dice que el parámetro debe ser una lista de elementos con cualquier tipo de objeto. Además, puede vincular el parámetro E para declarar referencias a elementos de la lista dentro del cuerpo de la función.

La lista como tipo de parámetro tiene la misma semántica, excepto que no hay forma de declarar referencias a los elementos en la lista que no sea usar Object. Otros mensajes dan diferencias sutiles adicionales.

+0

Disculpa, tengo un error en mi pregunta y he editado la pregunta. – Fio

+0

@Gene puede explicar por qué en el segundo parámetro de función debe ser una lista de objetos que se derivan de String. Como el parámetro no define su tipo de objeto "Lista list", entonces creo que puede contener cualquier tipo de objeto junto con String. –

+0

En lugar de decir "primero" o "segundo", incluya el código correspondiente para evitar confusiones (y edita el OP). –

5

Generics hace que la colección sea más segura.

List<E>: E aquí es el parámetro de tipo, que se puede utilizar para determinar el tipo de contenido de la lista, pero no había No manera de comprobar cuál era el contenido durante el runtime.

Generics are checked only during compilation time. 

<? extends String>: Esto fue especialmente incorporar en Java, para manejar el problema de la cual estaba con el parámetro de tipo. "? extends String" significa que esta lista puede tener

objects which IS-A String. 

Por ejemplo:

clase Animal clase Perro extiende la clase Animal tigre se extiende Animal

Así, utilizando "public void go(ArrayList<Animal> a)" se NOT accept perro o Tiger como su contenido, sino animal.

"public void go(ArrayList<? extends Animal> a)" es cuál es necesario para hacer el Check ArrayList take in Dog and Tiger type.

para las referencias de cabeza de Java.

+0

solo puedo usar? como se muestra en el ejemplo anterior Lista lista? entonces ¿qué tipo de objeto es puede almacenar –

+0

"?" se usa para hacer que la colección sea capaz de tomar los argumentos del método llamado que extiende el tipo mencionado aquí "". –

+0

Lista lista en este ejemplo el usuario no ha definido el tipo ... entonces, ¿qué tipo de objeto tomará? –

1

La primera es una función que acepta un parámetro que debe ser una lista de elementos de tipo E.

el segundo tipo no está definido ejemplo

List<?> list 

por lo que la lista de cualquier tipo de objeto puede pasar.

0

(Desde su edición) Esas dos firmas de función tienen el mismo efecto que el código externo; ambas toman cualquier List como argumento. Un comodín es equivalente a un parámetro de tipo que se usa solo una vez.

1

generalmente explico la diferencia entre < E> y < ?> mediante una comparación con cuantificaciones lógicas, es decir, cuantificación universal y cuantificación existencial.

  • corresponde a "forall E, ..."
  • corresponde a "existe algo (denotado por) de tal manera que ...."

Por lo tanto, ambas de las siguientes declaraciones genéricas método

function 1:

public static <E> void funct1 (List<E>; list1) { 

} 

function 2:

public static void funct2(List<?> list) { 

} 

significa que, para todo tipo E clase, define funct1, y, para algunos existen ing clase denotada por < ?>, definimos funct1.

0

Además de esas diferencias antes mencionadas, también hay una diferencia adicional: puede establecer de manera explícita los argumentos de tipo para la llamada del método genérico:

List<Apple> apples = ... 
ClassName.<Banana>funct2(apples); // for some reason the compiler seems to be ok 
           // with type parameters, even though the method has none 

ClassName.<Banana>funct1(apples); // compiler error: incompatible types: List<Apple> 
            //     cannot be converted to List<Banana> 

(ClassName es el nombre de la clase que contiene los métodos)

Cuestiones relacionadas