2011-07-30 6 views
8

¿Cuál es la diferencia en seguir 2 líneas?Límites del método genérico de Java

public static <T extends Comparable<? super T>> int methodX(List<T> data) 
public static <T> int methodX(List<? extends Comparable<? super T>> data) 

Respuesta

2

Su primera opción es una parametrización "más estricta". Es decir, está definiendo la clase T con un montón de restricciones, y luego úsela más adelante con List. En su segundo método, el parámetro clase T es genérico sin condiciones, y el parámetro de clase List s se define en términos del parámetro T.

La segunda forma es sintácticamente diferentes, así, con un ? en lugar de la primera opción de T, ya que en la definición del parámetro no está definiendo el parámetro de tipo T sino más bien su uso, por lo que el segundo método no puede ser tan específica .

La diferencia práctica que surge de esto es de herencia. Su primer método tiene que ser un tipo que es comparable a un super clase de sí mismo, mientras que el segundo tipo solamente tiene que ser comparable a un incondicional/no relacionada T:

public class Person implements Comparable<Number> { 
    @Override 
    public int compareTo(Number o) { 
     return 0; 
    } 
    public static <T extends Comparable<? super T>> int methodX(List<T> data) { 
      return 0; 
    } 
    public static <T> int methodY(List<? extends Comparable<? super T>> data) { 
      return 0; 
    } 
    public static void main(String[] args) { 
     methodX(new ArrayList<Person>()); // stricter ==> compilation error 
     methodY<Object>(new ArrayList<Person>()); 
    } 
} 

Si cambia el Comparable de Person para poder comparar Object o Person (el árbol de herencia de la clase base) luego methodX también funcionará.

0

El primer método espera una lista de elementos que se pueden comparar en contra de su propia clase o un supertipo de ella. Por ejemplo, los números reales pueden compararse con cualquier tipo de números:

class Real extends Number implements Comparable<Number> { 
    public int compareTo(Number o) ... 
} 

Un poco más restrictivo, pero todavía aceptable para su primer método es el siguiente:

class Real extends Number implements Comparable<Real> { 
    public int compareTo(Real o) ... 
} 

Sin embargo, el segundo método es en realidad no muy diferente de esta versión:

public static int methodY(List<? extends Comparable<?>> data) ... 

es decir, se puede reemplazar T con un comodín sin nombre ? porque sólo se usa una vez en la firma del método. No utiliza conceptos como la misma clase opropia clase de un objeto, etc.

1

Para las personas que llaman, la segunda versión es más o menos equivalente a

public static <T, X extends Comparable<? super T>> int methodX(List<X> data) 

Supongamos que una persona llama con una arg cuyo tipo List<Foo> concreto. La inferencia tipo concluirá que X=Foo.Entonces conseguimos una nueva ecuación sobre T de X 's obligado

=> 
Foo <: Comparable<? super T> 

(A <: B significa que A es un subtipo de B)

Si Foo es comparable en absoluto, es casi seguro que implements Comparable<Foo> [2]

=> 
Comparable<Foo> <: Comparable<? super T> 
=> 
T <: Foo 

Sin más información, la inferencia elige T=Foo.

Por lo tanto, desde el punto de vista del llamante, las dos versiones no son realmente diferentes.

Dentro del cuerpo del método, la segunda versión no tiene acceso al parámetro de tipo X, que es uno sintético introducido en la fase de compilación. Esto significa que solo puede leer desde data. Cosas como

X x = data.get(0); 
data.set(1, x); 

son imposibles en la versión n. ° 2; No hay tal problema en la versión n. ° 1 con T.

Sin embargo podemos adelante # 2 a # 1

<T1> method1(List<T1> data){ data.set(...); } 

<T2> method2(List<?...> data) 
{ 
    method1(data); 
} 
(they must have difference method names; overloading not allowed since java7) 

Esto se debe a que el compilador, el tipo de data es realmente List<X> (que conoce el secrete X), por lo que no hay ningún problema llamando method1(data) después inferir que T1=X

[1] JLS3, 5.1.10 Capture Conversión

[2] de acuerdo con el javadoc de Comparable , Esta interfaz impone un orden total en los objetos de cada clase que lo implementa. Eso significa que si Foo implements Comparable<W>, W debe ser Foo o un súper tipo de Foo. Es bastante improbable que una implementación de subclase defina un orden total entre los objetos de una súper clase. Entonces W definitivamente debería ser Foo. De lo contrario, pasarían cosas divertidas. El ejemplo notorio es 'Timestamp', su javadoc (ahora) explica por qué no se puede comparar con su supertipo Date

Cuestiones relacionadas