2008-11-20 9 views
227

Tengo problemas para navegar la regla de Java para inferir parámetros de tipo genérico. Considere la siguiente clase, que tiene un parámetro de lista opcional:Collections.emptyList() devuelve una lista <Object>?

import java.util.Collections; 
import java.util.List; 

public class Person { 
    private String name; 
    private List<String> nicknames; 

    public Person(String name) { 
    this(name,Collections.emptyList()); 
    } 

    public Person(String name,List<String> nicknames) { 
    this.name = name; 
    this.nicknames = nicknames; 
    } 
} 

Mi compilador Java da el siguiente error:

Person.java:9: The constructor Person(String, List<Object>) is undefined 

Pero Collections.emptyList() Devuelve el tipo <T> List<T>, no List<Object>. La adición de un reparto no ayuda

public Person(String name) { 
    this(name,(List<String>)Collections.emptyList()); 
} 

produce

Person.java:9: inconvertible types 

Usando EMPTY_LIST en lugar de emptyList()

public Person(String name) { 
    this(name,Collections.EMPTY_LIST); 
} 

rendimientos

Person.java:9: warning: [unchecked] unchecked conversion 

Considerando que el siguiente cambio MAK es el error desaparezca:

public Person(String name) { 
    this.name = name; 
    this.nicknames = Collections.emptyList(); 
} 

¿Puede alguien explicar por qué regla verificación de tipos Estoy corriendo en contra de aquí, y la mejor manera de trabajar alrededor de ella? En este ejemplo, el ejemplo del código final es satisfactorio, pero con clases más grandes, me gustaría poder escribir métodos siguiendo este patrón de "parámetro opcional" sin duplicar el código.

Para obtener crédito adicional: ¿cuándo es apropiado usar EMPTY_LIST en comparación con emptyList()?

+1

Para todas las preguntas relacionadas con Java Generics, recomiendo "[Java Generics and Collections] (http://oreilly.com/catalog/9780596527754/)" por Maurice Naftalin, Philip Wadler. –

Respuesta

389

El problema que encuentras es que aunque el método emptyList() devuelve List<T>, no lo has proporcionado con el tipo, por lo que el valor predeterminado es List<Object>.Puede suministrar el parámetro de tipo, y que su código se comporta como se esperaba, así:

public Person(String name) { 
    this(name,Collections.<String>emptyList()); 
} 

Ahora, cuando usted está haciendo la asignación recta, el compilador puede averiguar los parámetros de tipo genérico para usted. Se llama inferencia de tipo. Por ejemplo, si se hizo esto:

public Person(String name) { 
    List<String> emptyList = Collections.emptyList(); 
    this(name, emptyList); 
} 

entonces la llamada emptyList() sería correctamente devolver un List<String>.

+10

Entendido. Viniendo del mundo ML, es extraño para mí que Java no pueda inferir el tipo correcto: el tipo de parámetro formal y el tipo de retorno de emptyList son claramente unificables. Pero creo que el tipo de persona que habla solo puede dar "pasos de bebé". –

+5

En algunos casos simples, podría parecer posible que el compilador infiera el parámetro de tipo faltante en este caso, pero esto podría ser peligroso. Si existen varias versiones del método con diferentes parámetros, puede terminar llamando al incorrecto. Y el segundo podría no existir aún ... –

+7

Esa notación "Colecciones. emptyList()" es realmente extraña, pero tiene sentido. Más fácil que Enum >. :) –

85

que desea utilizar:

Collections.<String>emptyList(); 

Si nos fijamos en la fuente de lo que emptyList que hace ver que en realidad sólo lo hace un

return (List<T>)EMPTY_LIST; 
26

el método emptyList tiene esta firma:

public static final <T> List<T> emptyList() 

Eso <T> antes de la lista de palabras significa que infiere el valor de la g parámetro energético T del tipo de variable al que se asigna el resultado. Así que en este caso:

List<String> stringList = Collections.emptyList(); 

El valor de retorno es entonces hace referencia explícitamente por una variable de tipo List<String>, por lo que el compilador puede averiguarlo. En este caso:

setList(Collections.emptyList()); 

No hay ninguna variable de retorno explícito para el compilador a utilizar para averiguar el tipo genérico, por lo que el valor predeterminado es Object.

Cuestiones relacionadas