Чтение онлайн

ЖАНРЫ

Философия Java3

Эккель Брюс

Шрифт:

NonCollectionSequence nc = new NonCollectionSequence; InterfaceVsIterator.display(nc.iteratorO);

}

} /* Output:

0:Rat l:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx *///:-

Создание Iterator обеспечивает минимальную логическую привязку между последовательностью и методом, использующим эту последовательность, а также налагает гораздо меньше ограничений на класс последовательности, реализующий Collection.

Синтаксис foreach и итераторы

До настоящего момента «синтаксис foreach» использовался в основном с массивами, но он также будет работать с любым

объектом Collection. Некоторые примеры уже встречались нам при работе с ArrayList, но можно привести и более общее подтверждение:

//: holding/ForEachCollections java

// Синтаксис foreach работает с любыми коллекциями

import java.util.*,

public class ForEachCollections {

public static void main(String[] args) {

Collection<String> cs = new LinkedList<String>; Col lections.addAl1(cs,

"Take the long way home".splitC' ")); for(String s : cs)

System, out. pri nt(..... + s + .....),

}

} /* Output-

'Take' 'the' 'long' 'way' 'home' *///:-

Поскольку cs является Collection, этот пример показывает, что поддержка foreach является характеристикой всех объектов Collection.

Работа этой конструкции объясняется тем, что в Java SE5 появился новый интерфейс Iterable, который содержит метод iterator для создания Iterator, и именно интерфейс Iterable используется при переборе последовательности в синтаксисе foreach. Следовательно, создав любой класс, реализующий Iterable, вы сможете использовать его в синтаксисе foreach:

//: hoidi ng/IterableClass.java // Anything Iterable works with foreach. import java.util.*;

public class IterableClass implements Iterable<String> { protected StringE] words = ("And that is how " +

"we know the Earth to be banana-shaped.").splitC "); public Iterator<String> iteratorO {

return new Iterator<String> { private int index = 0; public boolean hasNextO {

return index < words length;

}

public String nextO { return words[index++]; } public void remove0 { // Not implemented

throw new UnsupportedOperationExceptionO,

};

public static void main(Stnng[] args) {

for(String s • new IterableClassO) System out print(s + " ");

}

} /* Output.

And that is how we know the Earth to be banana-shaped. *///:-

Метод iterator возвращает экземпляр анонимной внутренней реализации Iterator<string>, последовательно доставляющей каждое слово в массиве. В main мы видим, что IterableClass действительно работает в синтаксисе foreach.

В Java SE5 многие классы реализуют Iterable, прежде всего все классы Collection (но не Map). Например, следующий код выводит все переменные окружения (environment) операционной системы:

//: holding/Envi ronmentVariables.java import java util *;

public class EnvironmentVariables {

public static void main(String[] args) {

for (Map Entry entry System getenvO .entrySetO) { System.out.println(entry.getKey + ": " + entry. getValueO);

}

}

} /* (Выполните, чтобы увидеть результат) *///:-

System.getenv возвращает Map, entrySet создает Set с элементами Map.Entry, a Set поддерживает Iterable и поэтому

может использоваться в цикле foreach.

Синтаксис foreach работает с массивами и всем, что поддерживает Iterable, но это не означает, что массив автоматически поддерживает Iterable:

// hoiding/ArraylsNotIterable.java import java.util.*;

public class ArraylsNotlterable {

static <T> void test(Iterable<T> ib) { for(T t • ib)

System.out.print(t + " ");

}

public static void main(String[] args) { test(Arrays.asList(l. 2, 3)); StringC] strings = { "А", "В". "С" }: // Массив работает в foreach, но не является Iterable: //! test(strings);

// его необходимо явно преобразовать к Iterable: testCArrays.asLi st(stri ngs));

}

} /* Output: 1 2 3 А В С *///•-

Попытка передачи массива в аргументе Iterable завершается неудачей. Автоматическое преобразование в Iterable не производится; его необходимо выполнять вручную.

Идиома «метод-адаптер»

Что делать, если у вас имеется существующий класс, реализующий Iterable, и вы хотите добавить новые способы использования этого класса в синтаксисе foreach? Допустим, вы хотите иметь возможность выбора между перебором списка слов в прямом или обратном направлении. Если просто воспользоваться наследованием от класса и переопределить метод iterator, то существующий метод будет заменен и никакого выбора не будет.

Одно из решений этой проблемы основано на использовании идиомы, которую я называю «методом-адаптером». Термин «адаптер» происходит от одноименного паттерна: вы должны предоставить интерфейс, необходимый для работы синтаксиса foreach. Если у вас имеется один интерфейс, а нужен другой, проблема решается написанием адаптера. В данном случае требуется добавить к стандартному «прямому» итератору обратный, так что переопределение исключено. Вместо этого мы добавим метод, создающий объект Iterable, который может использоваться в синтаксисе foreach. Как будет показано далее, это позволит нам предоставить несколько вариантов использования foreach:

//: hoiding/AdapterMethodldiom.java

// Идиома "метод-адаптер" позволяет использовать foreach

// с дополнительными разновидностями Iterable.

import java.util.*;

class ReversibleArrayList<T> extends ArrayList<T> {

public ReversibleArrayList(Collection<T> c) { super(c); }. public Iterable<T> reversedO {

return new Iterable<T> {

public Iterator<T> iteratorO {

return new Iterator<T> {

int current = sizeO - 1,

public boolean hasNextO { return current > -1;

}

public T nextO { return get (current--); } public void removeO { // He реализован throw new

UnsupportedOperationExceptionO;

}

} •

}

}:

}

}

public class AdapterMethodldiom {

public static void main(String[] args) { ReversibleArrayList<String> ral =

new ReversibleArrayList<String>(

Arrays.asList(To be or not to be".splitC' "))): // Получаем обычный итератор, полученный при помощи iteratorO: forCString s : ral)

Поделиться с друзьями: