Iterador Java sobre una colección vacía de un tipo parametrizado
-
03-07-2019 - |
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.
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(){
}
}
}