Философия Java3
Шрифт:
Класс стека
Давайте рассмотрим менее тривиальный пример: реализацию традиционного стека. В главе И была приведена реализация стека на базе LinkedList. В этом примере класс LinkedList уже содержал все методы, необходимые для создания стека. Класс стека строился объединением одного параметризованного класса (Stack<T>) с другим параметризованным классом (LinkedList<T>). Этот пример показывает, что параметризованный тип — такой же тип, как и все остальные (за некоторыми исключениями, о которых речь пойдет
Вместо того, чтобы использовать LinkedList, мы также могли реализовать собственный механизм хранения связанного списка:
//: generics/LinkedStack java
// Стек, реализованный на базе внутренней структуры
public class LinkedStack<T> {
private static class Node<U> { U item; Node<U> next;
NodeO { item = null; next = null; } Node(U item, NodeO next) { this.item = item; this next = next;
}
boolean end { return item == null && next == null; }
}
private Node<T> top = new Node<T>; // Предохранитель public void push(T item) {
top = new Node<T>(item, top);
}
public Т popО {
Т result = top item; if( itop.endO)
top = top.next; return result;
}
public static void main(String[] args) {
LinkedStack<String> Iss = new LinkedStack<String>; for(String s : "Phasers on stun!".split(" ")) Iss.push(s);
String s;
while((s = Iss.popO) != null) System.out.printin(s);
}
} /* Output-
stun!
on
Phasers *///:-
Внутренний класс Node тоже является параметризованным и имеет собственный параметр типа.
Для определения наличия элементов в стеке в этом примере используется предохранитель (end sentinel). Он создается при конструировании LinkedStack, а затем при каждом вызове push новый объект Node<T> создается и связывается с предыдущим Node<T>. При вызове рор всегда возвращается top.item, после чего текущий объект Node<T> уничтожается и происходит переход к следующему — если только текущим элементом не является предохранитель; в этом случае переход не выполняется. При повторных вызовах рор клиент будет получать null, что свидетельствует об отсутствии элементов в стеке.
RandomList
Рассмотрим еще один пример контейнера: допустим, вам понадобилась особая разновидность списка, которая случайным образом выбирает один из своих элементов при вызове select. Так как класс должен работать для любых объектов, мы воспользуемся параметризацией:
//• generics/RandomList.java import java.util.*;
public class RandomList<T> {
private ArrayList<T> storage'= new ArrayList<T>, private Random rand = new Random(47); public void add(T item) { storage.add(item); } public T selectО {
return storage.get(rand.nextInt(storage.si ze));
}•
public static void main(String[] args) {
RandomList<String> rs = new RandomList<String>; for(String s: ("The quick brown fox jumped over " + "the lazy brown dog").splitC "))
rs. add (s); продолжение & for(int i = 0; i < 11; i++)
System.out.printers.select
}
} /* Output:
brown over fox quick quick dog brown The brown lazy brown
Параметризованные интерфейсы
Параметризация работает и с интерфейсами. Например, класс, создающий объекты, называется генератором. В сущности, генератор представляет собой специализированную версию паттерна «метод-фабрика», но при обращении к нему никакие аргументы не передаются, тогда как метод-фабрика обычно получает аргументы. Генератор умеет создавать объекты без дополнительной информации.
Обычно генератор определяет всего один метод — тот, который создает объекты. Назовем его next и включим в стандартный инструментарий:
//• net/mi ndvi ew/uti1/Generator.java
// Параметризованный интерфейс
package net.mi ndvi ew.uti1;
public interface Generator<T> { T nextO; } ///:-
Возвращаемое значение метода next параметризовано по типу Т. Как видите, механизм параметризации работает с интерфейсами почти так же, как с классами.
Чтобы продемонстрировать, как работает реализация Generator, мы воспользуемся иерархией классов, представляющих разные виды кофе:
//: generics/coffee/Coffee.java package generics.coffee;
public class Coffee {
private static long counter = 0; private final long id = counter**; public String toStringO {
return getClassO.getSimpleNameО + " " + id:
}
} Hill-. generics/coffee/Latte.java package generics.coffee; public class Latte extends Coffee {} Hill \ generics/coffee/Mocha.java package generics.coffee; public class Mocha extends Coffee {} Hill : generics/coffee/Cappuccino.java package generics.coffee; public class Cappuccino extends Coffee {} Hill : generics/coffee/Americano.java package generics.coffee;
public class Americano extends Coffee {} /// ~
//. generics/coffee/Breve java
package generics coffee.
public class Breve extends Coffee {} ///:-
Теперь мы можем реализовать интерфейс Generator<Coffee>, который создает случайные типы объектов из иерархии Coffee:
// generics/coffee/CoffeeGenerator.java
// Генератор случайных объектов из иерархии Coffee-
package generics coffee.
import java util *,
import net mindview util.*,