Iteratore Java su una raccolta vuota di un tipo con parametri
-
03-07-2019 - |
Domanda
In Java, devo restituire un Iteratore dal mio metodo. I miei dati provengono da un altro oggetto che di solito può darmi un iteratore, quindi posso semplicemente restituirlo, ma in alcune circostanze i dati sottostanti sono nulli. Per coerenza, voglio restituire un & Quot; vuoto & Quot; in questo caso iteratore, quindi i miei chiamanti non devono testare null.
Volevo scrivere qualcosa del tipo:
public Iterator<Foo> iterator() {
if (underlyingData != null) {
return underlyingData.iterator(); // works
} else {
return Collections.emptyList().iterator(); // compiler error
}
}
Ma il compilatore Java si lamenta del ritorno Iterator<Object>
invece di Iterator<Foo>
. Anche il cast su (Iterator<Foo>)
non funziona.
Soluzione
Puoi ottenere un elenco vuoto di tipo Foo attraverso la seguente sintassi:
return Collections.<Foo>emptyList().iterator();
Altri suggerimenti
Java 7 è in circolazione da molto tempo ormai. A meno che tu non stia sviluppando per una versione precedente di Java, restituiresti un iteratore vuoto come questo:
return Collections.emptyIterator();
Andrei più lungo le linee di
public Iterator<Foo> iterator() {
if (underlyingData == null)
return Collections.<Foo> emptyList().iterator();
return underlyingData.iterator();
}
Solo, gestisci il caso speciale e ritorna, quindi gestisci il caso normale. Ma il mio punto principale è che puoi evitare l'incarico con
Collections.<Foo> emptyList().iterator();
Il fastidio di Collections.<Foo>emptyList().iterator()
è il motivo principale per cui forniamo Iterators.emptyIterator()
in google-collections . Nessun parametro di tipo necessario, in casi come il tuo.
Immagino che ciò dimostri che l'inferenza del tipo Java non funziona in tutti i casi e che l'operatore ternario non è sempre equivalente al costrutto if-else apparentemente equivalente.
Voglio anche dichiarare evitare null
. Evita anche di passare Iterator
s, perché hanno strani comportamenti con stato (preferisci Iterable
). Tuttavia, supponendo che tu abbia una ragione legittima, non prematura per farlo, il mio modo preferito di scriverlo sarebbe
public Iterator<Foo> iterator() {
return getUnderlyingData().iterator();
}
private List<Foo> getUnderlyingData() {
if (underlyingData == null) {
return Collections.emptyList();
} else {
return underlyingData;
}
}
IMO, è meglio non inserire le informazioni di tipo inferibile se possono essere dedotte (anche se allunga il codice).
Quasi sicuramente lo farai più di una volta, quindi inserisci un metodo getUnderlyingData
invece di dichiarare semplicemente una variabile locale.
Stai chiamando iterator
su entrambi i risultati, quindi non ripeterti.
Scusa, l'ho capito. Devi usare un compito in modo che il compilatore possa capire il tipo con parametri.
public Iterator<Foo> iterator() {
if (underlyingData != null) {
return underlyingData.iterator();
} else {
List<Foo> empty = Collections.emptyList(); // param type inference
return empty.iterator();
}
}
public final class EmptyIterator{
public static Iterator iterator(){
return new Empty();
}
private static class Empty implements Iterator {
public boolean hasNext(){
return false;
}
public Object next(){
throw new NoSuchElementException();
}
public void remove(){
}
}
}