Pregunta

En Java, necesito devolver un iterador de mi método. Mis datos provienen de otro objeto que generalmente me puede dar un iterador para que pueda devolverlo, pero en algunas circunstancias los datos subyacentes son nulos. Por coherencia, quiero devolver un & Quot; vacío & Quot; iterador en ese caso para que mis llamantes no tengan que probar nulo.

Quería escribir algo como:

public Iterator<Foo> iterator() {
   if (underlyingData != null) {
      return underlyingData.iterator();  // works
   } else {
      return Collections.emptyList().iterator();  // compiler error
   }
}

Pero el compilador de Java se queja de la devolución de Iterator<Object> en lugar de Iterator<Foo>. El envío a (Iterator<Foo>) tampoco funciona.

¿Fue útil?

Solución

Puede obtener una lista vacía del tipo Foo a través de la siguiente sintaxis:

return Collections.<Foo>emptyList().iterator();

Otros consejos

Java 7 ha estado fuera por mucho tiempo ahora. A menos que esté desarrollando para una versión anterior de Java, devolvería un iterador vacío como este:

return Collections.emptyIterator();

Iría más en la línea de

public Iterator<Foo> iterator() {
    if (underlyingData == null)
        return Collections.<Foo> emptyList().iterator();
    return underlyingData.iterator();
}

Simplemente, maneje el caso especial y regrese, luego maneje el caso normal. Pero mi punto principal es que puedes evitar la asignación con

Collections.<Foo> emptyList().iterator();

La molestia de Collections.<Foo>emptyList().iterator() es la razón principal por la que proporcionamos Iterators.emptyIterator() en google-collections . No se necesita parámetro de tipo, en casos como el suyo.

Supongo que esto muestra que la inferencia de tipos Java no funciona en todos los casos y que el operador ternario no siempre es equivalente a la construcción if-else aparentemente equivalente.

También quiero indicar evitar null . También evite pasar Iterator s, porque tienen un comportamiento extraño con estado (prefiera Iterable). Sin embargo, suponiendo que tenga un motivo legítimo y no prematuro para hacerlo, mi forma preferida de escribirlo sería

public Iterator<Foo> iterator() {
    return getUnderlyingData().iterator();
}
private List<Foo> getUnderlyingData() {
    if (underlyingData == null) {
        return Collections.emptyList();
    } else {
        return underlyingData;
    }
}

En mi opinión, es mejor no insertar la información de tipo inferible si se puede inferir (incluso si hace que su código sea más largo).

Es casi seguro que lo hará más de una vez, por lo tanto, inserte un método getUnderlyingData en lugar de simplemente declarar una variable local.

Está llamando a iterator en ambos resultados, así que no se repita.

Lo siento, lo descubrí. Debe usar una asignación para que el compilador pueda determinar el tipo parametrizado.

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(){
        }
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top