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.

È stato utile?

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(){
        }
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top