2012-03-07 7 views
8

Para una interfaz genérica:¿Qué significa realmente el comodín del parámetro de tipo de Java? ¿Cuál es la verdadera diferencia entre Foo y Foo <?>?

public interface Foo<T> { 
    void f(T t); 
} 

La diferencia entre los dos campos:

public class Bar { 
    Foo foo1; 
    Foo<?> foo2; 
} 

Es eso foo2 es un tipo genérico y foo no lo es. Como ? es un comodín (que creo que se refiere a cualquier tipo) y cada tipo es un subtipo de Objeto, entonces espero que Foo<?> y Foo<Object> sean equivalentes semánticamente y sintácticamente.

Sin embargo, echa un vistazo a los siguientes:

public class Puzzler { 
    void f() { 
     Integer i = null; 
     Foo<?> foo1 = null; 
     foo1.foo(i); // ERROR 
     Foo foo2 = null; 
     foo2.foo(i); // OKAY 
     Foo<Integer> foo3 = null; 
     foo3.foo(i); // OKAY 
     Foo<Object> foo4 = null; 
     foo4.foo(i); // OKAY 
    } 

    private interface Foo<T> { 
     void foo(T t); 
    } 
} 

Así Foo<?>Foo<Object> y son no la misma sintácticamente.

¿Qué está pasando aquí? Estoy bastante atrapado tratando de entender esto.

+0

asumiendo que estos son errores de compilación? – paislee

+0

@andrewcooke Ese código sería válido; sin embargo, dado que el objeto se almacenaría en una variable 'Foo ', no conocería el parámetro 'String'. En consecuencia, si 'Foo' tiene algún método que tome argumentos de tipo' T', el único valor permitido sería 'null', y si tiene algún método que devuelva' T', se trata como 'Object'. – Taymon

+0

lo siento, borré mi comentario antes de ver su respuesta. ahora estoy confundido cuando funciona la captura? –

Respuesta

6

Foo<?> es semánticamente lo mismo que Foo<? extends Object>: es un Foo con el parámetro tipo de algo específico, pero el único que se sabe acerca de "algo" es que es cierta subclase de Object (que no es decir demasiado, ya todas las clases son subclases de Object). Foo<Object>, por otro lado, es un Foo con un parámetro de tipo específicamente Object. Aunque todo es compatible con la asignación con Object, no todo será compatible con la asignación con ? donde ? se extiende a Object.

He aquí un ejemplo de por qué Foo<?> debería generar un error:

public class StringFoo implements Foo<String> { 
    void foo(String t) { . . . } 
} 

Ahora cambiar su ejemplo de esto:

Foo<?> foo1 = new StringFoo(); 

Desde i es un Integer, no hay manera de que el compilador debe permitir foo1.foo(i) para compilar.

Tenga en cuenta que

Foo<Object> foo4 = new StringFoo(); 

también se compila de acuerdo con las reglas para matching parameterized types desde Object y String son demostrablemente tipos distintos.

Foo (sin el tipo de parámetro en absoluto — en bruto) se debe considerar un error de programación. De acuerdo con el Java Language Specification (§4.8), sin embargo, el compilador acepta dicho código para no romper el código heredado no genérico.

Debido a type erasure, nada de esto hace la diferencia para generar el código de bytes. Es decir, las únicas diferencias entre estos son en tiempo de compilación.

+0

despejó la confusión perfectamente, gracias – mwsltn

+0

Los compiladores * no pueden * tratar los tipos sin procesar como errores, consulte [el JLS] (http://docs.oracle.com /javase/specs/jls/se7/html/jls-4.html#jls-4.8) –

+0

@Daniel: en Eclipse, puede configurar el compilador para que trate el uso de tipos sin formato como errores. También puede configurar la mayoría de los compiladores para tratar todas las advertencias como errores y los compiladores pueden advertir sobre el uso de tipos sin procesar. –

0

Bueno, debido a la borradura de tipo, todo lo relacionado con los genéricos es solo de tiempo de compilación; Supongo que eso es lo que estás llamando sintáctico.
Creo que su evaluación inicial es correcta y la diferencia real es que la convierte en una variable de tipo genérico y Foo simple no lo hace.

0

Considere estos tipos:

  • List<Object>
  • List<CharSequence>
  • List<String>

Aunque String es un subtipo de CharSequence que es un subtipo de Object, este tipo de lista no tienen cualquier relación subtipo-supertipo. (Curiosamente, String[] es un subtipo de CharSequence[] que es un subtipo de Object[], pero eso es por razones históricas.)

Supongamos que queremos escribir un método que imprime una lista. Si hacemos

void print(List<Object> list) {...} 

esto no va a ser capaz de imprimir un List<String> (sin cortes), ya que un List<String> no es un List<Object>. Pero con comodines, podemos escribir

void print(List<?> list) {...} 

y pasarle cualquier Lista.

Los comodines pueden tener límites superior e inferior para una mayor flexibilidad. Digamos que queremos imprimir una lista que contiene solo CharSequence s. Si hacemos

void print(List<CharSequence> list) {...} 

continuación, nos encontramos con el mismo problema - sólo podemos pasarle un List<CharSequence>, y nuestra List<String> no es un List<CharSequence>. Pero si en vez hacemos

void print(List<? extends CharSequence> list) {...} 

Entonces podemos pasar esta una List<String>, y una List<StringBuilder>, y así sucesivamente.

0

El comodín en Foo<?> indica que dentro del alcance actual, usted no sabe ni le importa qué tipo de 'Foo' tiene.

Foo<?> y Foo<? extends Object> son lo mismo (el primero es la abreviatura de otro). Foo<Object> es diferente.

Un ejemplo concreto:

Puede asignar cualquier tipo de List a List<?> por ejemplo,

List<?> list1 = new ArrayList<String>(); 
List<?> list2 = new ArrayList<Object>(); 
List<?> list3 = new ArrayList<CharSequence>(); 

Si usted tiene un List<?> puede llamar size(), ya que no necesita saber qué tipo de lista es para averiguar su tamaño.Y puede llamar al get(i) porque sabemos que la lista contiene algún tipo de Object, por lo que el compilador lo tratará como si get regresara y Object.
Pero no puede llamar al add(o) porque no sabe (y el compilador no sabe) con qué tipo de lista está tratando.
En nuestro ejemplo anterior que no le gustaría para permitir list1.add(new Object()); debido a que se supone que es una lista de String s

La razón de comodines es lo que puede hacer cosas como esta:

public static boolean containsNull(List<?> list) 
{ 
    for(Object o : list) 
    { 
     if(o == null) return true; 
    } 
    return false; 
} 

Ese código puede trabajar en cualquier tipo de lista que desea, un List<String>, List<Object>, List<Integer>, etc.

Si la firma era public static boolean containsNull(List<Object> list) entonces sólo podía pasar List<Object> a ella, List<String> no funcionaría.

Cuestiones relacionadas