Философия Java3 - Брюс Эккель
Шрифт:
Интервал:
Закладка:
Вспомогательный класс Set
Рассмотрим еще один пример использования параметризованных методов: математические операции между множествами. Эти операции удобно определить в виде параметризованных методов, используемых с различными типами:
//: net/mi ndvi ew/uti1/Sets.java package net.mindview.util; import java.util.*;
public class Sets {
public static <T> Set<T> union(Set<T> a, Set<T> b) { Set<T> result = new HashSet<T>(a);
result.addAl1(b). return result;
}
public static <T>
Set<T> intersection(Set<T> a. Set<T> b) { Set<T> result = new HashSet<T>(a). result retainAll(b); return result;
}
// Вычитание подмножества из надмножества-public static <T> Set<T> difference(Set<T> superset. Set<T> subset) {
Set<T> result = new HashSet<T>(superset); result.removeAll(subset). return result;
}
// Дополнение -- все. что не входит в пересечение public static <T> Set<T> complement(Set<T> a, Set<T> b) {
return difference(union(a. b). intersection^, b));
}
} ///:-
Первые три метода дублируют первый аргумент, копируя его ссылки в новый объект HashSet, поэтому аргументы Set не изменяются напрямую. Таким образом, возвращаемое значение представляет собой новый объект Set.
Четыре метода представляют математические операции с множествами: union() возвращает объект Set, полученный объединением множеств-аргументов, intersection() возвращает объект Set с общими элементами аргументов, difference() вычисляет разность множеств, a complement) — объект Set со всеми элементами, не входящими в пересечение. Чтобы создать простой пример использования этих методов, мы воспользуемся перечислением, содержащим разные названия акварельных красок:
//: generics/watercolors/Watercolors java package generics.watercolors;
public enum Watercolors {
ZINC. LEM0N_YELL0W. MEDIUM_YELLOW, DEEP_YELLOW, ORANGE. BRILLIANT_RED. CRIMSON. MAGENTA. ROSE_MADDER. VIOLET. CERULEAN_BLUE_HUE. PHTHALO_BLUE, ULTRAMARINE. COBALT_BLUE_HUE, PERMANENT_GREEN. VIRIDIAN_HUE. SAP_GREEN. YELL0W_0CHRE. BURNT_SIENNA. RAWJJMBER, BURNTJJMBER. PAYNES_GRAY. IVORY_BLACK } ///:-
Для удобства (чтобы избежать уточнения всех имен) в следующем примере это перечисление импортируется статически. Мы используем EnumSet — новый инструмент Java SE5 для простого создания Set на базе перечисления. Статическому методу EnumSet.range() передаются первый и последний элементы диапазона, по которому строится множество:
II: generics/WatercolorSets.java import generics.watercolors.*; import java.util.*;
import static net.mindview.util.Print.*; продолжение &
import static net.mindview util.Sets *; import static generics.watercolors.Watercolors.*;
public class WatercolorSets {
public static void main(String[] args) { Set<Watercolors> setl =
EnumSet.range(BRILLIANT_RED, VIRIDIAN_HUE); Set<Watercolors> set2 =
EnumSet range(CERULEAN_BLUE_HUE, BURNT_UMBER), printCsetl. " + setl); print("set2- " + set2);
print("union(setl. set2)- " + union(setl. set2)); Set<Watercolors> subset = intersection(setl. set2); print("intersection(setl. set2). " + subset). print("difference(setl. subset)- " +
difference(setl. subset)). print("difference(set2. subset). " +
difference(set2. subset)); print("complement(setl. set2) " + complement(setl. set2));
}
} /* Output.
setl. [BRILLIANT_RED. CRIMSON. MAGENTA. ROSE_MADDER, VIOLET. CERULEAN_BLUE_HUE. PHTHALO_BLUE. ULTRAMARINE. COBALT_BLUE_HUE. PERMANENT_GREEN. VIRIDIAN_HUE] set2- [CERULEAN_BLUE_HUE, PHTHALO_BLUE. ULTRAMARINE. COBALT_BLUE_HUE. PERMANENT_GREEN. VIRIDIAN_HUE. SAP_GREEN. YELL0W_0CHRE. BURNT_SIENNA. RAWJJMBER. BURNT_UMBER] union(setl. set2) [SAP_GREEN. ROSE_MADDER, YELL0W_0CHRE. PERMANENT_GREEN. BURNTJJMBER, COBALT_BLUE_HUE, VIOLET. BRILLIANT_RED. RAWJJMBER. ULTRAMARINE. BURNT_SIENNA. CRIMSON. CERULEAN_BLUE_HUE. PHTHALO_BLUE. MAGENTA. VIRIDIAN_HUE]
intersection(setl. set2): [ULTRAMARINE. PERMANENT_GREEN. COBALT_BLUE_HUE. PHTHALO_BLUE. CERULEAN_BLUE_HUE. VIRIDIAN_HUE]
difference(setl, subset): [ROSE_MADDER, CRIMSON. VIOLET. MAGENTA. BRILLIANT_RED] difference(set2. subset): [RAWJJMBER, SAP_GREEN. YELLOW J3CHRE, BURNT_SIENNA, BURNTJJMBER]
complement(setl, set2): [SAP_GREEN. ROSE_MADDER. YELL0W_0CHRE. BURNTJJMBER, VIOLET. BRILLIANT_RED, RAW_UMBER. BURNT_SIENNA. CRIMSON. MAGENTA] *///:-
В выходных данных показаны результаты выполнения каждой операции. В следующем примере представлены варианты вызова Sets.difference() для разных классов Collection и Map из java.util:
//: net/mi ndvi ew/uti1/Contai nerMethodDi fferences.java package net.mindview.util; import java lang reflect *; import java.util.*,
public class ContainerMethodDifferences {
static Set<String> methodSet(Class<?> type) {
Set<String> result = new TreeSet<String>(); for (Method m : type.getMethodsO) result.add(m.getNameO); return result;
}
static void interfaces(Class<?> type) {
System.out.print("Interfaces in " +
type getSimpleNameO + ": "); List<String> result = new ArrayList<String>();
for(Class<?> с : type.getlnterfacesO) result.add(c.getSimpleName()); System.out.println(result);
}
static Set<String> object = methodSet(Object.class); static { object.add("clone"); } static void
differencesass<?> superset, Class<?> subset) {
System.out.pri nt(superset.getSimpleName() +
" extends " + subset.getSimpleNameO + ", adds: "); Set<String> comp = Sets.difference(
methodSet(superset). methodSet(subset)); сотр.removeAll(object); // He показывать методы 'Object' System.out.println(comp); interfaces(superset),
}
public static void main(String[] args) {
System.out.printlnC'Collection: " +
methodSet(Collection.class)), interfaces(Collection.class); difference(Set.class. Collection.class); difference(HashSet.class. Set.class); difference(LinkedHashSet.class, HashSet.class); difference(TreeSet.class. Set.class); differences st. class. Col 1 ecti on.cl ass); difference(ArrayList.class, List.class); differences nkedLi st. class. List.class); difference(Queue.class. Collection.class); di fference(Pri ori tyQueue.class. Queue.class); System.out.println("Map: " + methodSet(Map.class)); difference(HashMap.class. Map.class); difference(LinkedHashMap.class. HashMap.class); difference(SortedMap.class. Map.class); difference(TreeMap.class, Map class);
}
} ///:-
Анонимные внутренние классы
Параметризация также может применяться к внутренним классам и анонимным внутренним классам. Пример реализации интерфейса Generator с использованием анонимных внутренних классов:
//: generics/BankTeller.java
II Очень простая имитация банковского обслуживания.
import java.util.*;
import net.mindview.util.*;
class Customer {
private static long counter = 1; private final long id = counter++; private Customer() {}
public String toStringO { return "Customer " + id; } // Метод для получения объектов Generator: public static Generator<Customer> generatorO { return new Generator<Customer>()
public Customer nextO { return new CustomerO; }
class Teller {
private static long counter = 1; private final long id = counter++; private TellerO {}
public String toStringO { return "Teller " + id; } // Синглетный объект Generator: public static Generator<Teller> generator = new Generator<Teller>() {
public Teller next О { return new TellerO; }
}:
}
public class BankTeller {
public static void serve(Teller t, Customer c) {
System.out.printin(t + " обслуживает " + с);
}
public static void main(String[] args) { Random rand = new Random(47); Queue<Customer> line = new LinkedList<Customer>(); Generators.fillOine, Customer, generator 0, 15): List<Teller> tellers = new ArrayList<Teller>(); Generators.filKtellers, Teller.generator, 4); for(Customer с : line)
serve(tellers.get(rand.nextlnt(tellers.size())), c);
}
} /* Output:
Teller
3
обслуживает
Customer
1
Teller
2
обслуживает
Customer
2
Teller
3
обслуживает
Customer
3
Teller
1
обслуживает
Customer
4
Teller
1
обслуживает
Customer
5
Teller
3
обслуживает
Customer
6
Teller
1
обслуживает
Customer
7
Teller
2
обслуживает
Customer
8
Teller
3
обслуживает
Customer
9
Teller
3
обслуживает
Customer
10
Teller
2
обслуживает
Customer
11
Teller
4
обслуживает
Customer
12
Teller
2
обслуживает
Customer
13
Teller
1
обслуживает
Customer
14
Teller
1
обслуживает
Customer
15
*///•-
И Customer, и Teller содержат приватные конструкторы, поэтому для создания их объектов пользователь вынужден использовать объекты Generator. Customer содержит метод generator(), который при каждом вызове создает новый объект Generator<Customer>. На случай, если множественные объекты Generator вам не понадобятся, в Teller создается синглетный открытый объект generator. Оба подхода продемонстрированы в вызовах fill() внутри main().
Поскольку метод generator() в Customer и объект Generator в Teller являются статическими, они не могут быть частью интерфейса, поэтому «обобщить» эту"
конкретную идиому не удастся. Несмотря на это обстоятельство, она достаточно хорошо работает в методе fill().
Построение сложных моделей
К числу важных преимуществ параметризации относится простота и надежность создания сложных моделей. Например, можно легко создать список (List) с элементами-кортежами:
//: generics/TupleList.java
// Построение сложных параметризованных типов путем объединения
import java.util.*;
import net.mindview util.*;
public class TupleList<A,B.C.D> extends ArrayList<FourTuple<A,B,C.D» {
public static void main(String[] args) {
TupleList<Vehicle, Amphibian. String. Integer> tl =
new TupleList<Vehicle. Amphibian, String. Integer>(); tl.add(TupleTest.hO); tl.add(TupleTest.hO):
for(FourTuple<Vehicle.Amphibian.String.Integer> v tl) System.out.println(i);
}
} /* Output:
([email protected] [email protected], hi. 47) ([email protected], [email protected] hi. 47) *///.-
Запись получается довольно громоздкой (особенно при создании итератора), однако вы получаете довольно сложную структуру данных без излишков программного кода.
А вот другой пример, который показывает, как легко строить сложные модели на основе параметризованных типов. Хотя каждый класс представляет собой автономный «строительный блок», их совокупность имеет сложную структуру. В данном случае моделируется магазин с товарами, полками и стеллажами:
//. generics/Store.java
// Построение сложной модели на базе параметризованных контейнеров, import java util *. import net.mindview.util.*.
class Product {
private final int id. private String description; private double price;
public Product(int IDnumber, String descr. double price){ id = IDnumber; description = descr, this.price = price; System.out.pri ntln(toString());
}
public String toStringO {
return id + " + description + ", цена: $" + price;
public void priceChange(double change) { price += change,
}
public static Generator<Product> generator = new Generator<Product>() {
private Random rand = new Random(47), public Product next О {
return new Product(rand nextlnt(lOOO), "Test",
Math round(rand nextDoubleO * 1000 0) + 0 99).
class Shelf extends ArrayList<Product> { public Shelf(int nProducts) {
Generators fill(this. Product generator. nProducts),
class Aisle extends ArrayList<Shelf> {
public AisleCint nShelves, int nProducts) { for(int i = 0; i < nShelves; i++) add(new Shelf(nProducts)),
class CheckoutStand {} class Office {}
public class Store extends ArrayList<Aisle> {
private ArrayList<CheckoutStand> checkouts =
new ArrayList<CheckoutStand>(); private Office office = new OfficeO. public Store(int nAisles, int nShelves, int nProducts) { for(int i = 0; i < nAisles; i++)