2010-06-17 7 views
47

lo tanto, tengo una clase con un constructor de esta manera:¿Por qué el uso de Collections.emptySet() con genéricos funciona en la asignación pero no como un parámetro de método?

public FilterList(Set<Integer> labels) { 
    ... 
} 

y quiero construir un nuevo FilterList objeto con un conjunto vacío. Siguiendo el consejo de Joshua Bloch en su libro Effective Java, no quiero crear un nuevo objeto para el conjunto vacío; Voy a usar Collections.emptySet() lugar:

FilterList emptyList = new FilterList(Collections.emptySet()); 

Esto me da un error, quejándose de que no es un java.util.Set<java.lang.Object>java.util.Set<java.lang.Integer>. OK, qué tal esto:

FilterList emptyList = new FilterList((Set<Integer>)Collections.emptySet()); 

¡Esto también me da un error! Bien, ¿qué tal esto:

Set<Integer> empty = Collections.emptySet(); 
FilterList emptyList = new FilterList(empty); 

Hey, funciona! ¿Pero por qué? Después de todo, Java no tiene inferencia de tipo, por lo que obtienes una advertencia de conversión no verificada si haces Set<Integer> foo = new TreeSet() en lugar de Set<Integer> foo = new TreeSet<Integer>(). Pero Set<Integer> empty = Collections.emptySet(); funciona sin siquiera una advertencia. ¿Porqué es eso?

+2

todas las respuestas a continuación son correctos, pero lo que no entiendo es: ¿por qué se inicialice su colección con una lista vacía en lugar de llamar a un constructor predeterminado sin parámetros? Cada colección que conozco tiene un constructor con una colección existente y un constructor vacío. –

+0

Curiosamente, lo siguiente HACE compilar: 'FilterList emptyList = new FilterList ((Set ) (Establecer ) Collections.emptySet());' – EricS

+1

Otro ejemplo bastante divertido: 'Set emptySet = (Set ) Collections.emptySet(); 'no se compila. – neo

Respuesta

106

La respuesta corta es que eso es una limitación de la inferencia de tipo en el sistema genérico de Java. Puede inferir tipos genéricos contra variables concretas, pero no contra parámetros de método.

Yo sospecho esto es porque los métodos se envían de forma dinámica en función de la clase en tiempo de ejecución del objeto propietario, por lo que en tiempo de compilación (cuando todo información genérica se resuelve) puede en realidad no saber con certeza lo que la clase del parámetro del método será, y por lo tanto no puede inferir. Las declaraciones de variables son agradables y constantes, por lo que puedes.

Alguien más podría darte más detalles y/o un buen enlace. :-)

En cualquier caso, siempre se puede especificar los parámetros de tipo explícita para las llamadas genéricas de este modo:

Collections.<Integer>emptySet(); 

o incluso varios parámetros a la vez, por ejemplo,

Collections.<String, Boolean>emptyMap(); // Returns a Map<String, Boolean> 

Esto a menudo se ve un poco más limpio que tener que emitir, en los casos en que la inferencia no entra.

+1

+1 para una buena explicación de por qué. Me gustaría poder marcar dos respuestas correctas en SO :) – jdmichal

+3

Sé que el compilador no mirará la invocación de método para el contexto al inferir, pero no estoy seguro de por qué. Al menos para métodos privados o 'finales', debería ser posible hacer inferencias. –

+2

@Hank: o métodos 'estáticos', que se resuelven en tiempo de compilación también. Aún así, * no *, que supongo que es el punto principal. –

5

que quieres hacer esto:

FilterList emptyList = new FilterList(java.util.Collections.<Integer>emptySet()); 

Esto le indica al método emptySet que su parámetro genérico explícitamente debe por Integer en lugar del predeterminado Object. Y sí, la sintaxis es completamente funky y no intuitiva para esto. :)

+1

No estaba al tanto de ese sintex, gracias! Pero todavía me pregunto por qué esa sintaxis no es necesaria en la asignación de variables. –

+0

Ver la respuesta de Andrzej Doyle. Creo que es una buena explicación. – jdmichal

+0

No necesita 'nuevo' allí. De hecho, no creo que compile con eso. –

6

tratar

FilterList emptyList = new FilterList(Collections.<Integer>emptySet()); 

Puede forzar el parámetro de tipo de métodos que los tienen, en los casos en que la inferencia no es lo suficientemente bueno, o para permitir su uso subtipos; por ejemplo:

// forces use of ArrayList as parameter instead of the infered List 
List<String> l = someObject.<ArrayList<String> methodThatTakesTypeParamForReturnType(); 
Cuestiones relacionadas