Collections.emptyList () restituisce un elenco <oggetto>?


269

Sto riscontrando problemi nella navigazione della regola di Java per inferire i parametri di tipo generico. Considera la seguente classe, che ha un parametro elenco opzionale:

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

Il mio compilatore Java fornisce il seguente errore:

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

Ma Collections.emptyList()restituisce il tipo <T> List<T>, no List<Object>. L'aggiunta di un cast non aiuta

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

i rendimenti

Person.java:9: inconvertible types

Usando EMPTY_LISTinvece diemptyList()

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

i rendimenti

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

Considerando che la seguente modifica fa scomparire l'errore:

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

Qualcuno può spiegare quale regola di controllo del tipo sto incontrando qui, e il modo migliore per aggirare il problema? In questo esempio, l'esempio di codice finale è soddisfacente, ma con classi più grandi, mi piacerebbe poter scrivere metodi seguendo questo modello di "parametro opzionale" senza duplicare il codice.

Per un credito extra: quando è appropriato utilizzare EMPTY_LISTinvece di emptyList()?


1
Per tutte le domande relative a Java Generics, consiglio vivamente " Java Generics and Collections " di Maurice Naftalin, Philip Wadler.
Julien Chastang,

Risposte:


447

Il problema che stai riscontrando è che, sebbene il metodo emptyList()ritorni List<T>, non hai fornito il tipo, quindi per impostazione predefinita viene restituito List<Object>. Puoi fornire il parametro type e far sì che il tuo codice si comporti come previsto, in questo modo:

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

Ora, quando esegui un'assegnazione diretta, il compilatore può capire i parametri di tipo generico per te. Si chiama inferenza di tipo. Ad esempio, se hai fatto questo:

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

quindi la emptyList()chiamata restituirà correttamente a List<String>.


12
Fatto. Proveniente dal mondo ML, è strano per me che Java non possa inferire il tipo corretto: il tipo di parametro formale e il tipo restituito di emptyList sono chiaramente unificabili. Ma immagino che il tipo inferencer possa fare solo "piccoli passi".
Chris Conway,

5
In alcuni casi semplici, potrebbe sembrare possibile che il compilatore deduca in questo caso il parametro di tipo mancante, ma ciò potrebbe essere pericoloso. Se esistessero più versioni del metodo con parametri diversi, si potrebbe finire per chiamare quella sbagliata. E il secondo potrebbe non esistere ancora ...
Bill Michell,

13
Quella notazione "Collezioni. <String> emptyList ()" è davvero strana, ma ha un senso. Più facile di Enum <E estende Enum <E>>. :)
Thiago Chaves il

12
Non è più necessario fornire un parametro di tipo in Java 8 (a meno che non vi sia un'ambiguità in possibili tipi generici).
Vitalii Fedorenko,

9
Il secondo frammento mostra bene l'inferenza di tipo ma ovviamente non verrà compilato. La chiamata a thisdeve essere la prima istruzione nel costruttore.
Arjan,

99

Vuoi usare:

Collections.<String>emptyList();

Se guardi alla fonte per quale lista vuota vedi che in realtà fa solo una

return (List<T>)EMPTY_LIST;

26

il metodo emptyList ha questa firma:

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

Che <T>prima della parola Elenco significa che determina il valore del parametro generico T dal tipo di variabile a cui è assegnato il risultato. Quindi in questo caso:

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

Il valore restituito viene quindi indicato esplicitamente da una variabile di tipo List<String>, in modo che il compilatore possa capirlo. In questo caso:

setList(Collections.emptyList());

Non esiste una variabile di ritorno esplicita che il compilatore possa utilizzare per capire il tipo generico, quindi per impostazione predefinita è Object.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.