Me encuentro con un código Java como este:
public interface Foo<E> {}
public interface Bar<T> {}
public interface Zar<?> {}
¿Cuál es la diferencia entre los tres anteriores y cómo llaman a este tipo de declaraciones de clase o interfaz en Java?
Me encuentro con un código Java como este:
public interface Foo<E> {}
public interface Bar<T> {}
public interface Zar<?> {}
¿Cuál es la diferencia entre los tres anteriores y cómo llaman a este tipo de declaraciones de clase o interfaz en Java?
Respuestas:
Bueno, no hay diferencia entre los dos primeros: solo usan nombres diferentes para el parámetro de tipo ( Eo T).
La tercera no es una declaración válida: ?se usa como comodín que se usa cuando se proporciona un argumento de tipo , por ejemplo, List<?> foo = ...significa que se foorefiere a una lista de algún tipo, pero no sabemos qué.
Todo esto es genérico , que es un tema bastante importante. Es posible que desee conocerlo a través de los siguientes recursos, aunque, por supuesto, hay más disponibles:
Ty Eson solo identificadores. Podrías escribir KeyValuePair<K, V>por ejemplo. ?Sin embargo, tiene un significado especial.
Es más convención que cualquier otra cosa.
T está destinado a ser un tipo Eestá destinado a ser un Elemento ( List<E>: una lista de Elementos) Kes clave (en a Map<K,V>) V es Valor (como valor de retorno o valor asignado) Son totalmente intercambiables (a pesar de los conflictos en la misma declaración).
Las respuestas anteriores explican los parámetros de tipo (T, E, etc.), pero no explican el comodín, "?", O las diferencias entre ellos, así que abordaré eso.
Primero, para ser claros: los parámetros comodín y tipo no son lo mismo. Cuando los parámetros de tipo definen un tipo de variable (por ejemplo, T) que representa el tipo para un ámbito, el comodín no: el comodín simplemente define un conjunto de tipos permitidos que puede usar para un tipo genérico. Sin ningún límite ( extendso super), el comodín significa "usar cualquier tipo aquí".
El comodín siempre viene entre paréntesis angulares, y solo tiene significado en el contexto de un tipo genérico:
public void foo(List<?> listOfAnyType) {...} // pass a List of any type
Nunca
public <?> ? bar(? someType) {...} // error. Must use type params here
o
public class MyGeneric ? { // error
public ? getFoo() { ... } // error
...
}
Se vuelve más confuso donde se superponen. Por ejemplo:
List<T> fooList; // A list which will be of type T, when T is chosen.
// Requires T was defined above in this scope
List<?> barList; // A list of some type, decided elsewhere. You can do
// this anywhere, no T required.
Hay mucha superposición en lo que es posible con las definiciones de métodos. Los siguientes son, funcionalmente, idénticos:
public <T> void foo(List<T> listOfT) {...}
public void bar(List<?> listOfSomething) {...}
Entonces, si hay superposición, ¿por qué usar uno u otro? A veces, honestamente es solo estilo: algunas personas dicen que si no necesita un parámetro de tipo, debe usar un comodín solo para hacer que el código sea más simple / legible. Una diferencia principal que expliqué anteriormente: los parámetros de tipo definen una variable de tipo (por ejemplo, T) que puede usar en cualquier otra parte del alcance; el comodín no. De lo contrario, hay dos grandes diferencias entre los parámetros de tipo y el comodín:
Los params de tipo pueden tener múltiples clases limitantes; el comodín no puede:
public class Foo <T extends Comparable<T> & Cloneable> {...}
El comodín puede tener límites inferiores; los params tipo no pueden:
public void bar(List<? super Integer> list) {...}
En lo anterior, se List<? super Integer>define Integercomo un límite inferior en el comodín, lo que significa que el tipo de Lista debe ser Entero o un supertipo de Entero. El límite de tipo genérico está más allá de lo que quiero cubrir en detalle. En resumen, le permite definir qué tipos puede ser un tipo genérico. Esto hace posible el tratamiento de genéricos polimórficos. Por ejemplo con:
public void foo(List<? extends Number> numbers) {...}
Puede pasar un List<Integer>, List<Float>, List<Byte>, etc., para numbers. Sin delimitación de tipos, esto no funcionará, así son los genéricos.
Finalmente, aquí hay una definición de método que usa el comodín para hacer algo que no creo que pueda hacer de otra manera:
public static <T extends Number> void adder(T elem, List<? super Number> numberSuper) {
numberSuper.add(elem);
}
numberSuperpuede ser una Lista de números o cualquier supertipo de Número (por ejemplo, List<Object>), y elemdebe ser Número o cualquier subtipo. Con todos los límites, el compilador puede estar seguro de que .add()es seguro.
Una variable de tipo, <T>, puede ser cualquier tipo no primitivo que especifique: cualquier tipo de clase, cualquier tipo de interfaz, cualquier tipo de matriz o incluso otra variable de tipo.
Los nombres de parámetros de tipo más utilizados son:
En Java 7 se permite crear instancias de esta manera:
Foo<String, Integer> foo = new Foo<>(); // Java 7
Foo<String, Integer> foo = new Foo<String, Integer>(); // Java 6
Los nombres de parámetros de tipo más utilizados son:
E - Element (used extensively by the Java Collections Framework)
K - Key
N - Number
T - Type
V - Value
S,U,V etc. - 2nd, 3rd, 4th types
Verá estos nombres utilizados en la API Java SE
el compilador realizará una captura para cada comodín (p. ej., signo de interrogación en la Lista) cuando cree una función como:
foo(List<?> list) {
list.put(list.get()) // ERROR: capture and Object are not identical type.
}
Sin embargo, un tipo genérico como V estaría bien y lo convertiría en un método genérico :
<V>void foo(List<V> list) {
list.put(list.get())
}