Философия Java3
Шрифт:
}
} /* 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, в результате мы получаем многоуровневую архитектуру контейнеров, не лишаясь преимуществ безопасности типов и управляемости. Впечатляет и то, что построение такой модели не потребует заметных умственных усилий.
Тайна стирания
Когда вы приступаете к более глубокому изучению контейнеров, некоторые обстоятельства на первых порах выглядят довольно странно. Например,
//: 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(
list.getClass.getTypeParameters)); System out println(Arrays.toString(
map. getClassO .getTypeParametersO)). Л
продолжение &
System out pri ntinCArrays.toStri ng(
qua rk.getClass.getTypePa rameters));
System.out.pri ntinCArrays.toStri ng(
p.getClass.getTypePa rameters));
}
} /* Output: [E]
[K. V] [Q]
[POSITION. MOMENTUM] *///:-
Согласно документации JDK, Class.getTypeParameters «возвращает массив объектов TypeVariable, представляющих переменные типов, указанные в параметризованном объявлении...» Казалось бы, по ним можно определить параметры типов — но, как видно из результатов, вы всего лишь узнаете, какие идентификаторы использовались в качестве заполнителей, а эта информация не представляет особого интереса.
Мы приходим к холодной, бездушной истине:
Информация о параметрах типов недоступна внутри параметризованного кода.
Таким образом, вы можете узнать идентификатор параметра типа и ограничение параметризованного типа, но фактические параметры типов, использованные для создания конкретного экземпляра, остаются неизвестными. Этот факт, особенно раздражающий программистов с опытом работы на С++, является основной проблемой, которую приходится решать при использовании параметризации в Java.