Рейтинговые книги
Читем онлайн Философия Java3 - Брюс Эккель

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 60 61 62 63 64 65 66 67 68 ... 132

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)

System.out.print(s + " "); System.out printlnO;

// Передаем выбранный нами Iterable forCString s • ral .reversedO)

System.out.print(s + " "),

}

} /* Output To be or not to be be to not or be To */// ~

Если просто поместить объект ral в синтаксис foreach, мы получим (стандартный) «прямой» итератор. Но если вызвать для объекта reversed(), поведение изменится.

Использовав этот прием, можно добавить в пример IterableClass.java два метода-адаптера:

// hoidi ng/MultiIterableClass.java // Adding several Adapter Methods, import java util *;

public class MultilterableClass extends IterableClass { public Iterable<String> reversedO {

return new Iterable<String>() {

public Iterator<String> iteratorO {

return new Iterator<String>() {

int current = words length - 1,

public boolean hasNextO { return current > -1;

}

public String nextO { return words[current--];

}

public void removeО { // He реализован throw new

UnsupportedOperationException(),

}

}:

}

}.

}

public Iterable<String> randomizedO { return new Iterable<String>() {

public Iterator<String> iteratorO { List<String> shuffled =

new ArrayList<String>(Arrays.asList(words)); Collections.shuffleCshuffled, new Random(47)); return shuffled.iterator();

}

}:

}

public static void main(String[] args) {

MultilterableClass mic = new MultiIterableClassO; for (String s : mic. reversedO)

System out print(s + " "): System, out. pri ntlnO. for(String s : mic.randomizedO)

System out.print(s + " "); System.out.prmtlnO: продолжение & for(String s : mic)

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

}

} /* Output:

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

Из выходных данных видно, что метод Collections.shuffle не изменяет исходный массив, а только переставляет ссылки в shuffled. Так происходит только потому, что метод randomized() создает для результата Arrays.asList() «обертку» в виде ArrayList. Если бы операция выполнялась непосредственно с объектом List, полученным от Arrays.asList(), то это привело бы к изменению нижележащего массива:

//- hoiding/ModifyingArraysAsList.java import java util.*;

public class ModifyingArraysAsList {

public static void main(String[] args) {

Random rand = new Random(47);

Integer[] ia = { 1, 2, 3. 4, 5, 6. 7, 8. 9, 10 },

List<Integer> listl =

new ArrayList<Integer>(Arrays.asList(ia));

System.out.printIn("До перестановки. " + listl);

Col 1ecti ons.shuff1e(1i st1, rand);

System.out.println("После перестановки: " + listl);

System.out.printlnf'Массив: " + Arrays.toString(ia)),

List<Integer> list2 = Arrays.asList(ia);

System.out.println("До перестановки: " + list2);

Col 1 ecti ons. shuffled i st2. rand);

System.out.println("После перестановки: " + list2);

System.out.println("Массив: " + Arrays.toString(ia));

}

} /* Output:

До перестановки: [1, 2, 3. 4, 5. 6. 7, 8, 9, 10] После перестановки: [4. 6, 3, 1. 8, 7, 2, 5. 10. 9] Массив: [1, 2, 3. 4. 5. 6. 7, 8. 9. 10] До перестановки: [1, 2. 3, 4, 5, 6. 7. 8, 9, 10] После перестановки: [9, 1. 6. 3. 7, 2. 5, 10, 4, 8] Массив- [9. 1. 6. 3. 7, 2, 5. 10. 4. 8] *///:-

В первом случае вывод Arrays.asList() передается конструктору ArrayList(), а последний создает объект ArrayList, ссылающийся на элементы ia. Перестановка этих ссылок не изменяет массива. Но, если мы используем результат Arrays.asList(ia) напрямую, перестановка изменит порядок ia. Важно учитывать, что Arrays.asList() создает объект List, который использует нижележащий массив в качестве своей физической реализации. Если с этим объектом List выполняются какие-либо изменяющие операции, но вы не хотите изменения исходного массива, создайте копию в другом контейнере.

Резюме

В Java существует несколько способов хранения объектов:

• В массивах объектам назначаются числовые индексы. Массив содержит объекты заранее известного типа, поэтому преобразование типа при выборке объекта не требуется. Массив может быть многомерным и может использоваться для хранения примитивных типов. Тем не менее изменить размер созданного массива невозможно.

• В Collection хранятся отдельные элементы, а в Map — пары ассоциированных элементов. Механизм параметризации позволяет задать тип объектов, хранимых в контейнере, поэтому поместить в контейнер объект неверного типа невозможно, и элементы не нуждаются в преобразовании типа при выборке. И Collection, и Map автоматически изменяются в размерах при добавлении новых элементов. В контейнерах не могут храниться примитивы, но механизм автоматической упаковки автоматически создает объектные «обертки», сохраняемые в контейнере.

• В контейнере List, как и в массиве, объектам назначаются числовые индексы — таким образом, массивы и List являются упорядоченными контейнерами.

• Используйте ArrayList при частом использовании произвольного доступа к элементам или LinkedList при частом выполнении операций вставки и удаления в середине списка.

• Поведение очередей и стеков обеспечивается контейнером LinkedList.

• Контейнер Map связывает с объектом не целочисленный индекс, а другой объект. Контейнеры HashMap оптимизированы для быстрого доступа, а контейнер TreeMap хранит ключи в отсортированном порядке, но уступает по скорости HashMap. В контейнере LinkedHashMap элементы хранятся в порядке вставки, но хеширование обеспечивает быстрый доступ.

• В контейнере Set каждый объект может храниться только в одном экземпляре. Контейнер HashSet обеспечивает максимальную скорость поиска, а в TreeSet элементы хранятся в отсортированном порядке. В контейнере LinkedHashSet элементы хранятся в порядке вставки.

• Использовать старые классы Vector, Hashtable и Stack в новом коде не нужно.

Контейнеры Java — необходимый инструмент, которым вы будете постоянно пользоваться в своей повседневной работе; благодаря им ваш код станет более простым, мощным и эффективным. Возможно, на освоение некоторых аспектов контейнеров потребуется время, но вы быстро привыкнете к классам этой библиотеки и начнете использовать их.

Обработка ошибок и исключения

Один из основополагающих принципов философии Java состоит в том, что «плохо написанная программа не должна запускаться

В идеале ошибки должны обнаруживаться во время компиляции, перед запуском программы. Однако не все ошибки удается выявить в это время. Остальные проблемы приходйтся решать во время работы программы, с помощью механизма, который позволяет источнику ошибки передать необходимую информацию о ней получателю — а последний справляется с возникшими трудностями.

1 ... 60 61 62 63 64 65 66 67 68 ... 132
На этой странице вы можете бесплатно читать книгу Философия Java3 - Брюс Эккель бесплатно.
Похожие на Философия Java3 - Брюс Эккель книги

Оставить комментарий