2011-08-09 14 views
11

Con tipos parametrizados en Java, ¿cómo funcionan las reglas que comprueban si un parámetro está dentro de su trabajo vinculado exactamente para comodines?¿Cuáles son las condiciones formales para que un parámetro comodín en un tipo genérico Java esté dentro de sus límites?

dado una clase como esta:

class Foo<T extends Number> {} 

Experimentando con lo que el compilador acepta entera de que:

  • Se permite un ? extends comodín utilizando un tipo de interfaz sin relación: Foo<? extends Runnable> es válida
  • No se permite un comodín ? extends que utiliza un tipo de clase no relacionado: Foo<? extends Thread> no es válido. Eso tiene sentido porque ningún tipo puede ser un subtipo de ambos Number y Thread
  • En un comodín ? super, el límite inferior en el comodín debe ser subtipo de la cota de la variable Tipo: Foo<? super Runnable> no está permitido porque Runnable no es un subtipo de Number. Nuevamente, esta restricción tiene perfecto sentido.

¿Pero dónde se definen estas reglas? Mirando el Java Language Specification section 4.5, no veo nada que distinga las interfaces de las clases; y al aplicar mi interpretación de JLS, se dice que Foo<? super Runnable> es válido. Entonces probablemente entendí mal algo. Aquí está mi intento:

De esa sección de la JLS:

Un tipo parametrizado consiste en una clase o interfaz C nombre y un tipo real lista de argumentos < T1, ..., Tn>. Es un error de tiempo de compilación si C no es el nombre de una clase o interfaz genérica, o si el número de argumentos de tipo en la lista de argumentos de tipo real difiere del número de parámetros de tipo declarados de C. A continuación, cada vez que hablamos de una clase o tipo de interfaz, también incluimos la versión genérica, a menos que se excluya explícitamente. A lo largo de esta sección, deje que A1, ..., An sean los parámetros de tipo formales de C, y sea bi sea el límite declarado de Ai. La notación [Ai: = Ti] denota la sustitución de la variable de tipo Ai con el tipo Ti, por 1 < = i < = n, y se utiliza a lo largo de esta especificación.

Deje P = G < T1, ..., Tn> ser un tipo parametrizado. Debe ser el caso que, después de que P esté sujeto a la conversión de captura (§5.1.10) que resulta en el tipo G < X1, ..., Xn>, para cada argumento de tipo real Xi, 1 < = i < = n, Xi <: Bi [A1: = X1, ..., An: = Xn] (§4.10), o se produce un error de tiempo de compilación.

aplicar eso a P = Foo<? super Runnable>: que da C = Foo, n = 1, T1 = ? super Runnable y B1 = Number.

Para la conversión de captura de esta parte de la definition of capture conversion se aplica:

Si Ti es un tipo de argumento comodín de la forma? super Bi, entonces Si es una variable de tipo nuevo cuyo límite superior es Ui [A1: = S1, ..., An: = Sn] y cuyo límite inferior es Bi.

que da G < X1, ..., Xn> = Foo<X> donde X es una variable de tipo fresco con límite superior Number y límite inferior Runnable. No veo nada que prohíba explícitamente dicha variable de tipo. No hay variables de tipo en B1 = Number, por lo que Bi [A1: = X1, ..., An: = Xn] sigue siendo simplemente Number. X tiene Number como límite superior (que viene de la conversión de captura), y de acuerdo con the subtyping rules "Los supertipos directos de una variable de tipo son los tipos enumerados en su límite", por lo X <: Number (= Bi [A1: = X1, ..., An: = Xn]), por lo que este parámetro está dentro de sus límites. (¡Pero no lo es!)

Siguiendo el mismo razonamiento cada comodín está dentro de sus límites, por lo que algo aquí no está bien ... ¿Pero dónde exactamente salió mal este razonamiento? ¿Cómo do estas reglas funcionan cuando se aplican correctamente?

Respuesta

5

JLS en genéricos está incompleto, y atrapaste otro agujero en él. El límite inferior en las variables de tipo apenas se discute, y no veo ninguna restricción en la especificación ya sea en X que tiene límite superior Number y límite inferior Runnable. Probablemente lo dejaron fuera.

Intuitivamente, debe haber al menos un tipo posible que satisfaga tanto el límite superior y límite inferior de una variable de tipo, de lo contrario la variable y todos los tipos utilizando la variable serían inútiles. Como esto es casi seguro un error de programación, la compilación debería fallar.

Es fácil comprobar si el límite superior y el límite inferior forman un conjunto de tipos vacío. Se conocen todos los tipos súper del límite inferior; al menos uno de ellos debe ser el límite superior, de lo contrario no hay ningún tipo que esté dentro de ambos límites.

-

Los dos Foo<? extends A> casos se bien definidos en la especificación. Con la conversión de captura, tenemos nueva variable de tipo X con cota superior A & Number, y la especificación dice a una cota superior V1&...&Vm

Es un error en tiempo de compilación si para cualquier par de clases (no las interfaces) Vi y Vj, Vi no es una subclase de Vj o viceversa.

Por lo tanto, si A = Subproceso, la conversión de captura falla.

Cuestiones relacionadas