Java-итератор над пустой коллекцией параметризованного типа - PullRequest
28 голосов
/ 04 мая 2009

В Java мне нужно вернуть Iterator из моего метода. Мои данные поступают из другого объекта, который обычно может дать мне итератор, поэтому я могу просто вернуть его, но в некоторых случаях базовые данные равны нулю. Для согласованности я хочу вернуть «пустой» итератор в этом случае, чтобы мои вызывающие не проверяли нулевое значение.

Я хотел написать что-то вроде:

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

Но компилятор Java жалуется на возвращение Iterator<Object> вместо Iterator<Foo>. Приведение к (Iterator<Foo>) также не работает.

Ответы [ 7 ]

58 голосов
/ 04 мая 2009

Вы можете получить пустой список типа Foo через следующий синтаксис:

return Collections.<Foo>emptyList().iterator();
26 голосов
/ 17 мая 2013

Java 7 уже давно отсутствует. Если вы не разрабатываете для предыдущей версии Java, вы бы вернули пустой итератор, например:

return Collections.emptyIterator();
9 голосов
/ 04 мая 2009

Я бы пошел по линии

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

Просто обработайте специальный случай и вернитесь, затем обработайте обычный случай. Но главное, что вы можете избежать назначения с помощью

Collections.<Foo> emptyList().iterator();
4 голосов
/ 21 ноября 2009

Раздражение Collections.<Foo>emptyList().iterator() является основной причиной, по которой мы предоставляем Iterators.emptyIterator() в google-collection . В таких случаях, как ваш, параметр типа не требуется.

2 голосов
/ 05 мая 2009

Полагаю, это показывает, что логический тип Java не работает в каждом случае, и что троичный оператор не всегда эквивалентен явно эквивалентной конструкции if-else.

Я также хочу заявить избегать null. Также избегайте прохождения Iterator s, потому что у них странное поведение с состоянием (предпочитайте Iterable). Однако, если у вас есть законные, преждевременные причины для этого, мой предпочтительный способ написать это будет

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

ИМО, лучше не вставлять информацию о заменимом типе, если она может быть выведена (даже если она удлиняет ваш код).

Вы почти наверняка собираетесь сделать это более одного раза, поэтому вставьте метод getUnderlyingData вместо простого объявления локальной переменной.

Вы звоните iterator по обоим результатам, поэтому не повторяйте себя.

0 голосов
/ 13 марта 2013
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(){
        }
    }
}
0 голосов
/ 04 мая 2009

Извините, я понял это. Вам нужно использовать присваивание, чтобы компилятор мог определить параметризованный тип.

public Iterator<Foo> iterator() {
   if (underlyingData != null) {
      return underlyingData.iterator();
   } else {
      List<Foo> empty = Collections.emptyList();  // param type inference
      return empty.iterator();
   }
}
...