web-dev-qa-db-fra.com

Collections.emptyList () retourne une liste <Object>?

Je ne parviens pas à comprendre la règle de Java permettant d'inférer des paramètres de type générique. Considérez la classe suivante, qui a un paramètre de liste facultatif:

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;
  }
}

Mon compilateur Java génère l'erreur suivante:

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

Mais Collections.emptyList() retourne le type <T> List<T>, pas List<Object>. L'ajout d'un casting n'aide pas

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

les rendements

Person.Java:9: inconvertible types

Utiliser EMPTY_LIST au lieu de emptyList()

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

les rendements

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

Considérant que le changement suivant fait disparaître l'erreur:

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

Quelqu'un peut-il expliquer la règle de vérification de type que je rencontre ici et la meilleure façon de la contourner? Dans cet exemple, l'exemple de code final est satisfaisant, mais avec des classes plus grandes, j'aimerais pouvoir écrire des méthodes suivant ce modèle de "paramètre facultatif" sans dupliquer le code.

Pour un crédit supplémentaire: quand est-il approprié d'utiliser EMPTY_LIST par opposition à emptyList()?

254
Chris Conway

Le problème que vous rencontrez est que, même si la méthode emptyList() renvoie List<T>, vous ne l’avez pas fournie avec le type. Par conséquent, la valeur par défaut est List<Object>. Vous pouvez fournir le paramètre type et faire en sorte que votre code se comporte comme prévu:

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

Désormais, lorsque vous effectuez une affectation directe, le compilateur peut déterminer pour vous les paramètres de type génériques. C'est ce qu'on appelle l'inférence de type. Par exemple, si vous avez fait ceci:

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

alors l'appel emptyList() renverrait correctement un List<String>.

430
InverseFalcon

Vous voulez utiliser:

Collections.<String>emptyList();

Si vous regardez la source de quelle liste vide, voyez-vous qu’elle ne fait que

return (List<T>)EMPTY_LIST;
95
carson

la méthode emptyList a cette signature:

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

Ce <T> avant la liste de mots signifie qu'il déduit la valeur du paramètre générique T du type de variable à laquelle le résultat est attribué. Donc dans ce cas:

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

La valeur de retour est alors explicitement référencée par une variable de type List<String>, afin que le compilateur puisse la comprendre. Dans ce cas:

setList(Collections.emptyList());

Il n'y a pas de variable de retour explicite à utiliser par le compilateur pour déterminer le type générique. La valeur par défaut est donc Object.

26
Dan Vinton