Collections.emptyList() devuelve una Lista<Objeto>?

Resuelto Chris Conway asked hace 15 años • 4 respuestas

Tengo problemas para navegar por la regla de Java para inferir parámetros de tipo genérico. Considere la siguiente clase, que tiene un parámetro de lista opcional:

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 de 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>. Agregar un elenco no ayuda

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

rendimientos

Person.java:9: inconvertible types

Usando EMPTY_LISTen lugar deemptyList()

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

rendimientos

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

Mientras que el siguiente cambio hace que el error desaparezca:

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

¿Alguien puede explicar a qué regla de verificación de tipos me enfrento aquí y la mejor manera de solucionarla? En este ejemplo, el ejemplo de 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 código.

Para crédito adicional: ¿cuándo es apropiado utilizar EMPTY_LISTen lugar de emptyList()?

Chris Conway avatar Nov 21 '08 03:11 Chris Conway
Aceptado

El problema que está encontrando es que, aunque el método emptyList()devuelve List<T>, no le ha proporcionado el tipo, por lo que de forma predeterminada devuelve List<Object>. Puede proporcionar el parámetro de tipo y hacer que su código se comporte como se espera, así:

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

Ahora, cuando esté realizando una asignación directa, el compilador puede determinar los parámetros de tipo genérico por usted. Se llama inferencia de tipos. Por ejemplo, si hicieras esto:

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

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

InverseFalcon avatar Nov 20 '2008 20:11 InverseFalcon

Quieres usar:

Collections.<String>emptyList();

Si miras la fuente de qué lista vacía verás que en realidad solo hace un

return (List<T>)EMPTY_LIST;
carson avatar Nov 20 '2008 20:11 carson

el método lista vacía tiene esta firma:

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

Eso <T>antes de la palabra Lista significa que infiere el valor del parámetro genérico T del tipo de variable a la que está asignado el resultado. Entonces en este caso:

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

Luego, una variable de tipo hace referencia explícita al valor de retorno List<String>, para que el compilador pueda resolverlo. En este caso:

setList(Collections.emptyList());

No hay una variable de retorno explícita que el compilador pueda usar para determinar el tipo genérico, por lo que el valor predeterminado es Object.

Dan Vinton avatar Nov 20 '2008 20:11 Dan Vinton