Эккель Брюс
Шрифт:
}
} /* Output:
(Vehicle@llb86e7. Amphibian@35ce36, hi. 47) (Vehicle@757aef, Amphibian@d9f9c3. 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++)
add(new Aisle(nShelves. nProducts));
}
public String toStringO {
StringBuilder result = new StringBuilderO; for(Aisle a this)
for(Shelf s : a)
for(Product p • s) {
result.append(p); result.append("\n").
}
return result.toStringO.
}
public static void main(String[] args) {
System out.printin(new Store(14, 5. 10)).
}
} /* Output.
258: Test, цена: $400.99 861- Test, цена- $160.99 868: Test, цена: $417.99 207- Test, цена- $268.99 551- Test. цена. $114.99 278: Test, цена: $804.99
520. Test, цена: $554.99 140: Test, цена: $530.99
*///;-
Как видно из Store.toString, в результате мы получаем многоуровневую архитектуру контейнеров, не лишаясь преимуществ безопасности типов и управляемости. Впечатляет и то, что построение такой модели не потребует заметных умственных усилий.
Тайна стирания
Когда вы приступаете к более глубокому изучению контейнеров, некоторые обстоятельства на первых порах выглядят довольно странно. Например, запись ArrayList.class возможна, а запись ArrayList<Integer>.class — нет. Или возьмите следующий фрагмент:
//: generics/ErasedTypeEquivalence.java import java.util.*;
public class ErasedTypeEquivalence {
public static void main(String[] args) {
Class cl = new ArrayList<String>.getClassO: Class c2 = new ArrayList<Integer>.getClass, System.out.pri ntln(cl == c2);
}
} /* Output:
true
*///.-
Было бы логично считать, что ArrayList<String> и ArrayList<Integer> — разные типы, поэтому их поведение должно различаться, и при попытке поместить Integer в ArrayList<String> результат (неудача) должен отличаться от того, который будет получен при помещении Integer в ArrayList<Integer> (успех). Однако эта программа создает впечатление, что эти типы одинаковы. Следующий пример еще сильнее запутывает ситуацию:
//. generics/Lostlnformation.java import java util *.
class Frob {} class Fnorkle {} class Quark<Q> {}
class Particle<POSITION,MOMENTUM> {}
public class Lostlnformation {
public static void main(String[] args) {
List<Frob> list = new ArrayList<Frob>; Map<Frob,Fnorkle> map = new HashMap<Frob.Fnorkle>; Quark<Fnorkle> quark = new Quark<Fnorkle>; Particle<Long.Double> p = new Particle<Long.Double>: System.out.pri ntln(Arrays.toStri ng(