Подтвердить что ты не робот

Итератор Java над пустой коллекцией параметризованного типа

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

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

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>) тоже не работает.

4b9b3361

Ответ 1

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

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

Ответ 2

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

return Collections.emptyIterator();

Ответ 3

Я бы пошел дальше по строкам

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

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

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

Ответ 4

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

Ответ 5

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

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

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

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

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

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

Ответ 6

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

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

Ответ 7

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(){
        }
    }
}