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


269

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:

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 reparto no ayuda

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

rendimientos

Person.java:9: inconvertible types

Usando en EMPTY_LISTlugar 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 con qué regla de verificación de tipo me estoy enfrentando aquí y la mejor manera de evitarlo? 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 crédito adicional: ¿cuándo es apropiado usar EMPTY_LISTen lugar de emptyList()?


1
Para todas las preguntas relacionadas con Java Generics, recomiendo " Java Generics and Collections " de Maurice Naftalin, Philip Wadler.
Julien Chastang el

Respuestas:


447

El problema que está encontrando es que a pesar de que el método emptyList()regresa List<T>, no lo ha proporcionado con el tipo, por lo que el valor predeterminado es regresar 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 realiza una asignación directa, el compilador puede descubrir los parámetros de tipo genérico por usted. Se llama inferencia de tipos. Por ejemplo, si hiciste esto:

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

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


12
Entendido. Viniendo del mundo de 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 supongo que el inferenciador de tipos solo puede dar "pequeños pasos".
Chris Conway

55
En algunos casos simples, puede parecer posible que el compilador deduzca el parámetro de tipo faltante en este caso, pero esto podría ser peligroso. Si existieran varias versiones del método con diferentes parámetros, podría terminar llamando al incorrecto. Y el segundo quizás aún no exista ...
Bill Michell

13
Esa notación "Colecciones. <String> emptyList ()" es realmente extraña, pero tiene sentido. Más fácil que Enum <E extiende Enum <E>>. :)
Thiago Chaves

12
El suministro de un parámetro de tipo ya no es necesario en Java 8 (a menos que haya una ambigüedad en los posibles tipos genéricos).
Vitalii Fedorenko

9
El segundo fragmento muestra muy bien la inferencia de tipos pero, por supuesto, no se compilará. La llamada a thisdebe ser la primera instrucción en el constructor.
Arjan

99

Quieres usar:

Collections.<String>emptyList();

Si observa la fuente de qué emptyList, verá que en realidad solo hace un

return (List<T>)EMPTY_LIST;

26

El método emptyList tiene esta firma:

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

Que <T>antes de la palabra Lista significa que infiere el valor del parámetro genérico T del tipo de variable al que se asigna el resultado. Entonces en este caso:

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

El valor de retorno es referenciado explícitamente por una variable de tipo List<String>, para que el compilador pueda resolverlo. En este caso:

setList(Collections.emptyList());

No hay una variable de retorno explícita para que el compilador la use para descubrir el tipo genérico, por lo que su valor predeterminado es Object.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.