2011-01-14 8 views
9

Estoy leyendo "Genéricos en el lenguaje de programación Java" por Gilad Bracha y estoy confundido acerca de un estilo de declaración. El siguiente código se encuentra en la página 8:¿Alguien puede explicar la declaración de estos métodos genéricos de Java?

interface Collection<E> 
{ 
    public boolean containsAll(Collection<?> c); 
    public boolean addAll(Collection<? extends E> c); 
} 


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! 
} 

Mi punto de confusión proviene de la segunda declaración. No me queda claro cuál es el propósito de la declaración <T> sirve en la siguiente línea:

public <T> boolean containsAll(Collection<T> c); 

El método ya tiene un tipo (booleano) asociado a él.

¿Por qué usaría el <T> y qué le dice al compilador?

Creo que mi pregunta debe ser un poco más específica.

Por qué escribirías:

public <T> boolean containsAll(Collection<T> c); 

vs

public boolean containsAll(Collection<T> c); 

No es claro para mí, cuál es el propósito de <T> es, en la primera declaración de containsAll.

+1

Si comprende el PECS, entonces comprenderá esto. Consulte aquí http://stackoverflow.com/questions/4535930/regarding-pecs-java-generics –

+1

En Oracle/Sun JDK 6, se usa el primer ejemplo, por lo que no sé si usaría el segundo tampoco. –

+1

¿Y por qué es mejor que 'public boolean containsAll (Collection c);'? –

Respuesta

3

Por lo que puedo decir, en este caso <T> no proporciona nada útil.Crea un método que es completamente funcionalmente equivalente a aquellos que usan el comodín en su lugar.

Aquí hay un par de ejemplos en los que sería ser útiles:

public List<?> transform(List<?> in); 
//vs 
public <T> List<T> transform(List<T> in); 

En lo anterior, se puede correlacionar el tipo de retorno con el tipo de entrada. El primer ejemplo no puede correlacionar el tipo de tiempo de ejecución de los dos comodines.

public void add(List<?> list, Object obj); 
//vs 
public <T> void add(List<? super T> list, T obj); 

En lo anterior, el primer método no incluso ser capaz de añadir obj-list ya que no puede ser considerada como segura tipo. El parámetro genérico en el segundo asegura que list puede contener cualquier tipo obj.

3

El método ya tiene un tipo (booleano) asociado.

Ese es el retorno tipo. El tipo completo del método es "método que toma un parámetro Collection<T> (para algunos T) y devuelve un boolean".

Y aquí es donde entra T: el parámetro de la función lo usa. En otras palabras, este método se puede llamar con diferentes tipos como argumento. La única restricción de estos tipos es que deben implementar la interfaz Collection<T>, que a su vez se basa en un argumento genérico T (el tipo de los objetos almacenados en la colección).

+0

¿Pero por qué es esto mejor que el equivalente aparentemente funcional 'public boolean containsAll (Collection c)'? Del mismo modo, ¿por qué no 'public boolean addAll (Collection c)'? –

+0

Es mejor porque el cuerpo o la definición del método pueden referirse al tipo T, y el código puede evitar la conversión y el compilador puede garantizar la seguridad de los tipos. Ver mi respuesta para una explicación más detallada. –

+0

@Dave Costa: No veo una respuesta por su parte, pero si el uso de '' ayuda a ** otros ** métodos es irrelevante. Estamos hablando de ** estos ** métodos, donde no parece proporcionar ningún beneficio. –

0

Intente compilarlo sin el <T>.

Básicamente, le dice al compilador que este método contiene un genérico. No se requiere en el primer ejemplo porque? es un caso especial, y el segundo método hace referencia al tipo definido en la interfaz misma.

En una nota no relacionada, público no se requiere en una interfaz. Los métodos en una interfaz son públicos por defecto, por lo que pueden ahorrarle un poco de tipeo.

0

Declara el tipo genérico T utilizado por el método. Mientras que el tipo genérico E es el mismo para toda la interfaz T se limita al método para el que está declarado.

2

¿La? es simplemente un comodín. Significa que el método aceptará una Colección de cualquier tipo.

El <T> es un parámetro de tipo para el método. Básicamente, se asigna al comodín un nombre que luego se puede consultar en otra parte de la declaración y definición del método.

una mejor ilustración de la diferencia sería si el tipo de retorno del método variaba en función del tipo que se pasó.

que usted comenzó con un método como

Object getRandomElement(Collection<?> c)

Este aceptará cualquier colección, pero no hay manera de restringir su tipo de devolución. Por lo tanto, una persona que llama debería devolver el resultado al tipo que esperaba, lo que debería funcionar, pero genera advertencias de conversión de tipo inseguro.

Con un parámetro de tipo debe en su lugar escribir

<T> T getRandomElement(Collection<T> c)

En este caso, si se llama a este método con un Collection<String>, el compilador sabe que le proporcione una String.

+0

Bien dicho. Creo que una buena regla general es que el parámetro de tipo solo es necesario cuando tienes un tipo de retorno parametrizado (como 'Colecciones.emptyList() ') o cuando necesita correlacionar el tipo de devolución u otro parámetro con un parámetro parametrizado o genérico. –

Cuestiones relacionadas