Collections.emptyList() devuelve una lista?

5 minutos de lectura

avatar de usuario
chris conway

Tengo algunos 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 Java da el siguiente error:

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

Pero Collections.emptyList() tipo de retorno <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_LIST en vez de emptyList()

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 solucionarlo? 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 el código.

Para crédito adicional: cuándo es apropiado usar EMPTY_LIST Opuesto a emptyList()?

  • Para todas las preguntas relacionadas con Java Generics, recomiendo encarecidamente “Genéricos y colecciones de Javapor Maurice Naftalin, Philip Wadler.

    –Julien Chastang

    20 de noviembre de 2008 a las 20:39

El problema que está encontrando es que, aunque el método emptyList() devoluciones List<T>no le ha proporcionado el tipo, por lo que de forma predeterminada regresa 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 determinar 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 el emptyList() call devolvería correctamente un List<String>.

  • Entiendo. Viniendo del mundo de ML, me resulta extraño que Java no pueda inferir el tipo correcto: el tipo de parámetro formal y el tipo de retorno de lista vacía son claramente unificables. Pero supongo que el inferenciador de tipos solo puede dar “pasos de bebé”.

    – Chris Conway

    20 de noviembre de 2008 a las 20:43

  • 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 existieran varias versiones del método con diferentes parámetros, podría terminar llamando al incorrecto. Y es posible que el segundo ni siquiera exista todavía…

    – Bill Michell

    25 de noviembre de 2008 a las 13:47

  • Esa notación “Collections.emptyList()” es realmente extraña, pero tiene sentido. Más fácil que Enum>. 🙂

    – Thiago Chaves

    23 de junio de 2009 a las 17:07

  • Ya no es necesario proporcionar un parámetro de tipo en Java 8 (a menos que haya una ambigüedad en los posibles tipos genéricos).

    – Vitali Fedorenko

    06/04/2014 a las 20:10

  • El segundo fragmento muestra muy bien la inferencia de tipos pero, por supuesto, no se compilará. la llamada a this debe ser la primera declaración en el constructor.

    – Arjan

    10 de julio de 2016 a las 2:30

avatar de usuario
Carson

Quieres usar:

Collections.<String>emptyList();

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

return (List<T>)EMPTY_LIST;

  • respuesta impecable @carson

    – Gaurav

    9 de marzo a las 12:59

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

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

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

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

El valor de retorno es luego 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 averiguar el tipo genérico, por lo que el valor predeterminado es Object.

avatar de usuario
Lii

Desde Java 8, este tipo de código se compila como se esperaba y el compilador infiere el parámetro de tipo.

public Person(String name) {
    this(name, Collections.emptyList()); // Inferred to List<String> in Java 8
}

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

Lo nuevo en Java 8 es que el tipo de objetivo de una expresión se utilizará para inferir los parámetros de tipo de sus subexpresiones. Antes de Java 8, solo se usaban asignaciones directas y argumentos a métodos para la inferencia de parámetros de tipo.

En este caso, el tipo de parámetro del constructor será el tipo de destino para Collections.emptyList()y el tipo de valor devuelto se elegirá para que coincida con el tipo de parámetro.

Este mecanismo se agregó en Java 8 principalmente para poder compilar expresiones lambda, pero mejora las inferencias de tipos en general.

Java se está acercando a la correcta Hindley–Milner escriba la inferencia con cada lanzamiento!

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad