En el lenguaje de programación Java , el comodín ?
es un tipo especial de argumento de tipo [1] que controla la seguridad del uso de tipos genéricos (parametrizados). [2] Puede usarse en declaraciones e instanciaciones de variables, así como en definiciones de métodos, pero no en la definición de un tipo genérico. [3] [4] Esta es una forma de anotación de variación del sitio de uso , en contraste con las anotaciones de variación del sitio de definición que se encuentran en C# y Scala .
A diferencia de los arrays (que son covariantes en Java [2] ), las diferentes instancias de un tipo genérico no son compatibles entre sí, ni siquiera explícitamente. [2] Por ejemplo, las declaraciones Generic<Supertype> superGeneric; Generic<Subtype> subGeneric;
harán que el compilador informe errores de conversión tanto para conversiones como para (Generic<Subtype>)superGeneric
archivos (Generic<Supertype>)subGeneric
.
Esta incompatibilidad puede suavizarse con el comodín si ?
se utiliza como parámetro de tipo real. [2] Generic<?>
es un supertipo de todas las parametrizaciones del tipo genérico Generic
. Esto permite que los objetos de tipo Generic<Supertype>
y Generic<Subtype>
se asignen de forma segura a una variable o parámetro de método de tipo Generic<?>
. [2] El uso Generic<? extends Supertype>
permite lo mismo, restringiendo la compatibilidad con Supertype
sus hijos. [5] Otra posibilidad es Generic<? super Subtype>
, que también acepta ambos objetos y restringe la compatibilidad con Subtype
todos sus padres. [5]
En el cuerpo de una unidad genérica, el parámetro de tipo (formal) se maneja como su límite superior (expresado con extends
; Object
si no está restringido). [5] Si el tipo de retorno de un método es el parámetro de tipo, el resultado (por ejemplo, de tipo ?
) puede ser referenciado por una variable del tipo del límite superior (o Object
). En la otra dirección, el comodín no se ajusta a ningún otro tipo, ni siquiera Object
: si ?
se ha aplicado como parámetro de tipo formal de un método, no se le pueden pasar parámetros reales. Sin embargo, los objetos del tipo desconocido pueden leerse desde el objeto genérico y asignarse a una variable de un supertipo de límite superior.
Código de muestra para la clase:Generic<T extends UpperBound>
clase Genérico < T extiende UpperBound > { privado T t ; escritura nula ( T t ) { esto . t = t ; } T leer () { devolver t ; } }
Código de muestra que utiliza la clase:Generic<T extends UpperBound>
... final Genérico < Límite Superior > concreteTypeReference = new Genérico < Límite Superior > (); final Genérico <?> referencia comodín = referencia de tipo concreto ; límite superior final ub = referencia comodín . leer (); // El objeto también estaría bien comodínReferencia . escribir ( nuevo objeto ()); // escribe error comodínReferencia . escribir ( nuevo límite superior ()); // tipo de error concreteTypeReference . escribir ( nuevo límite superior ()); // DE ACUERDO ...
Un comodín acotado es aquel que tiene una restricción de herencia superior o inferior . El límite de un comodín puede ser un tipo de clase, un tipo de interfaz , un tipo de matriz o un tipo de variable. Los límites superiores se expresan mediante la palabra clave extends y los límites inferiores mediante la palabra clave super . Los comodines pueden indicar un límite superior o un límite inferior, pero no ambos.
Un límite superior en un comodín debe ser un subtipo del límite superior del parámetro de tipo correspondiente declarado en el tipo genérico correspondiente. [5] Un ejemplo de un comodín que establece explícitamente un límite superior es:
Generic<? extends SubtypeOfUpperBound> referenceConstrainedFromAbove;
Esta referencia puede contener cualquier parametrización Generic
cuyo argumento de tipo sea un subtipo de SubtypeOfUpperBound
. Un comodín que no indica explícitamente un límite superior es efectivamente lo mismo que uno que tiene la restricción extends Object
, ya que todos los tipos de referencia en Java son subtipos de Objeto.
Un comodín con un límite inferior, como por ejemplo
Generic<? super SubtypeOfUpperBound> referenceConstrainedFromBelow;
puede contener cualquier parametrización Generic
cuyo argumento any type sea a la vez un subtipo del límite superior del parámetro de tipo correspondiente y un supertipo de SubtypeOfUpperBound
. [5]
No se pueden crear objetos con un argumento de tipo comodín: por ejemplo, new Generic<?>()
está prohibido. En la práctica, esto es innecesario porque si uno quisiera crear un objeto que fuera asignable a una variable de tipo Generic<?>
, simplemente podría usar cualquier tipo arbitrario (que esté dentro de las restricciones del comodín, si corresponde) como argumento de tipo.
Sin embargo, new ArrayList<Generic<?>>()
está permitido porque el comodín no es un parámetro para el tipo instanciado ArrayList
. Lo mismo vale para new ArrayList<List<?>>()
.
En una expresión de creación de matriz, el tipo de componente de la matriz debe ser verificable según lo definido en la Especificación del lenguaje Java, Sección 4.7. Esto implica que, si el tipo de componente de la matriz tiene algún argumento de tipo, todos deben ser comodines ilimitados (comodines que constan solo de un ?
). Por ejemplo, new Generic<?>[20]
es correcto, mientras que new Generic<SomeType>[20]
no lo es.
En ambos casos, no utilizar parámetros es otra opción. Esto generará una advertencia ya que es menos seguro para los tipos (consulte Tipo sin formato ).
En Java Collections Framework, la clase List<MyClass>
representa una colección ordenada de objetos de tipo MyClass
. Los límites superiores se especifican usando extends
: A es una lista de objetos de alguna subclase de , es decir, se garantiza que cualquier objeto en la lista es de tipo , por lo que se puede iterar sobre él usando una variable de tipo [6]List<? extends MyClass>
MyClass
MyClass
MyClass
public void hacer algo ( Lista <? extiende MiClase > lista ) { para ( objeto final MiClase : lista ) { // OK // hacer algo } }
Sin embargo, no se garantiza que se pueda agregar cualquier objeto de tipo MyClass
a esa lista:
public void hacerAlgo ( Lista <? extiende MiClase > lista ) { final MiClase m = nueva MiClase (); lista . Agregame ) ; // Error de compilación }
Lo contrario es cierto para los límites inferiores, que se especifican usando super
: A es una lista de objetos de alguna superclase de , es decir, se garantiza que la lista podrá contener cualquier objeto de tipo , por lo que se puede agregar cualquier objeto de tipo :List<? super MyClass>
MyClass
MyClass
MyClass
public void hacerAlgo ( Lista <? super MiClase > lista ) { final MiClase m = nueva MiClase (); lista . Agregame ) ; // DE ACUERDO }
Sin embargo, no se garantiza que se pueda iterar sobre esa lista utilizando una variable de tipo MyClass
:
public void hacer algo ( Lista <? super MiClase > lista ) { para ( objeto final MiClase : lista ) { // Error de compilación // hacer algo } }
Para poder agregar objetos de tipo MyClass
a la lista e iterar sobre ellos usando una variable de tipo MyClass
, List<MyClass>
se necesita a, que es el único tipo List
que es ambos y . [7]List<? extends MyClass>
List<? super MyClass>
Los mnemotécnicos PECS (Producer Extends, Consumer Super) del libro Effective Java de Joshua Bloch brindan una manera fácil de recordar cuándo usar comodines (correspondientes a covarianza y contravarianza) en Java. [5]