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

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 73 74 75 76 77 78 79 80 81 ... 132

Для получения типизованного списка объектов Class требуется преобразование типа, что приводит к выдаче предупреждения на стадии компиляции. Метод loader() определяется отдельно и размещается в секции статической инициализации, потому что директива @SuppressWarnings не может располагаться прямо в секции статической инициализации.

Для подсчета объектов Pet нам понадобится механизм подсчета их разных видов. Для этой цели идеально подойдет карта (Map), в которой ключами являются имена типов Pet, а значениями — переменные Integer с количеством Pet. Например, это позволит получать ответы на вопросы типа «сколько существует объектов Hamster?». При подсчете Pet будет использоваться ключевое слово instanceof:

II: typeinfo/PetCount.java II Использование instanceof. import typeinfo.pets.*; import java util.*;

import static net.mindview.util.Print.*,

public class PetCount {

static class PetCounter extends HashMap<String,Integer> { public void count(String type) {

Integer quantity = get(type); if(quantity == null) put(type, 1);

else

put(type, quantity +1),

}

}

public static void

countPets(PetCreator creator) {

PetCounter counter= new PetCounter(), for(Pet pet : creator.createArray(20)) { // Подсчет всех объектов Pet: printnb(pet.getClass().getSimpleNameO + " "), if(pet instanceof Pet)

counter.count("Pet"); if(pet instanceof Dog)

counter.count("Dog"); if(pet instanceof Mutt)

counter count("Mutt"), if(pet instanceof Pug)

counter countC'Pug"); if(pet instanceof Cat)

counter.count("Cat"); if(pet instanceof Manx)

counter count("EgyptianMau"); if(pet instanceof Manx)

counter.count("Manx"); if(pet instanceof Manx)

counter.count("Cymric"). if(pet instanceof Rodent)

counter.count("Rodent"), if(pet instanceof Rat)

counter count("Rat"); if(pet instanceof Mouse)

counter.count("Mouse"); if(pet instanceof Hamster)

counter.count("Hamster"),

}

// Вывод результатов подсчета.

printO;

print(counter);

}

public static void main(String[] args) { countPets(new ForNameCreator());

}

} /* Output-

Rat Manx Cymric Mutt Pug Cymric Pug Manx Cymric Rat EgyptianMau Hamster EgyptianMau Mutt

Mutt Cymric Mouse Pug Mouse Cymric

{Pug=3. Cat=9, Hamster=l, Cymric=7, Mouse=2, Mutt=3, Rodent=5, Pet=20, Manx=7,

EgyptianMau=7, Dog=6, Rat=2}

*///:-

В countPets массив случайным образом заполняется объектами Pet с использованием PetCreator. Затем каждый объект Pet в массиве тестируется и подсчи-тывается при помощи instanceof.

У ключевого слова instanceof имеется одно серьезное ограничение: объект можно сравнивать только с именованным типом, но не с объектом Class. Возможно, вам показалось, что в предыдущем примере перебор всех выражений instanceof выглядит неудобно и громоздко, и вы правы. Тем не менее автоматизировать этот процесс невозможно — создать из объектов Class список ArrayList и сравнивать объекты по очереди с каждым его элементом не получится (к счастью, существует альтернативное решение, но это попозже). Впрочем, особенно горевать по этому поводу не стоит — если вам приходится записывать множество проверок instanceof, скорее всего, изъян кроется в архитектуре программы.

Использование литералов class

Если записать пример PetCreator.java с использованием литералов class, программа во многих отношениях становится более понятной:

//• typeinfo/pets/LiteralPetCreator.java // Using class literals package typeinfo pets, import java util.*;

public class LiteralPetCreator extends PetCreator { // Блок try не нужен @SuppressWarnings("unchecked")

public static final List<Class<? extends Pet» all Types = Col 1 ections unmodi fi ableLi st(Arrays.asLi st(

Pet.class, Dog class. Cat.class, Rodent.class, Mutt class. Pug.class. EgyptianMau.class. Manx.class. Cymric class. Rat.class. Mous'e.class.Hamster.class)); // Типы для случайного создания: private static final List<Class<? extends Pet» types = al1 Types.subLi st(al1 Types.i ndexOf(Mutt.class). allTypes.sizeO); public List<Class<? extends Pet» types() { return types;

}

public static void main(String[] args) { System.out.printin(types);

}

} /* Output:

[class typeinfo pets.Mutt, class typeinfo.pets.Pug. class typeinfo.pets.EgyptianMau. class typeinfo pets Manx, class typeinfo.pets.Cymric, class typeinfo.pets.Rat, class typeinfo.pets.Mouse, class typeinfo.pets.Hamster] *///.-

В будущем примере PetCount3.java контейнер Map заполняется всеми типами Pet (не только генерируемыми случайным образом), поэтому нам понадобился список allTypes. Список types представляет собой часть allTypes (создается вызовом List.subList()) со всеми типами Pet, поэтому он используется для случайного генерирования Pet.

На этот раз при создании types блок try не нужен, так как необходимые проверки типов проводятся еще во время компиляции и исключения не возбуждаются, в отличие от метода Class.forName().

Теперь библиотека typeinfo.pets содержит две реализации PetCreator. Чтобы вторая реализация использовалась по умолчанию, мы можем создать фасад (fagade), использующий LiteralPetCreator:

//• typeinfo/pets/Pets java

// Фасад для получения PetCreator по умолчанию

package typeinfo.pets, import java util *,

public class Pets {

public static final PetCreator creator =

new LiteralPetCreator(); public static Pet randomPetO {

return creator.randomPetO:

}

public static Pet[] createArray(int size) { return creator.createArray(size).

}

public static ArrayList<Pet> arrayListOnt size) { return creator arrayList(size);

}

} ///:-

При этом также обеспечиваются косвенные вызовы randomPet(), createArray() и arrayList().

Поскольку PetCount.countPets() получает аргумент PetCreator, мы можем легко проверить работу LiteralPetCreator (через представленный фасад):

II. typeinfo/PetCount2.java import typeinfo pets *.

public class PetCount2 {

public static void main(String[] args) { PetCount.countPets(Pets creator),

}

} /* (Выполните, чтобы увидеть результат) *///:-Результат будет таким же, как у PetCount.java.

Динамический вызов instanceof

Метод Class.islnstance() позволяет выполнить динамическую проверку типа объекта. Благодаря ему в примере PetCount.java наконец-то можно будет избавиться от нагромождения instanceof:

II: typeinfо/PetCount3.java

// Using isInstanceO

import typeinfo.pets.*:

import java.util.*,

import net.mindview.util *;

import static net.mindview util Print *;

public class PetCount3 {

static class PetCounter

extends LinkedHashMap<Class<? extends Pet>,Integer> { public PetCounter() {

super(MapData map(LiteralPetCreator.allTypes, 0)).

}

public void count(Pet pet) {

// Class.isInstanceO избавляет от множественных instanceof: for(Map Entry<Class<? extends Pet>.Integer> pair entrySetO) if(pair.getKey().islnstance(pet))

put(pair.getKey(). pair.getValueO + 1):

продолжение &

}

public String toStringO {

StringBuilder result = new StringBuilder("{"); for(Map.Entry<Class<? extends Pet>,Integer> pair : entrySetO) {

result.append(pai r.getKey().getSi mpleName());

result.append("=");

result.append(pai r.getValue());

result.appendC, ");

}

result.delete(result.1ength0 -2, result.1ength()); result.append("J"); return result.toStringO;

}

}

public static void main(String[] args) {

PetCounter petCount = new PetCounterO;

for(Pet pet : Pets.createArray(20)) {

printnbCpet.getClassO.getSimpleNameO + " "); petCount.count(pet);

}

printO;

print(petCount);

}

} /* Output:

Rat Manx Cymric Mutt Pug Cymric Pug Manx Cymric Rat EgyptianMau Hamster EgyptianMau Mutt Mutt Cymric Mouse Pug Mouse Cymric

{Pet=20, Dog=6. Cat-9. Rodent=5, Mutt-3. Pug=3. EgyptianMau=2, Manx=7, Cymric=5, Rat=2,

Mouse=2, Hamster=l}

*///:-

Для подсчета всех разновидностей Pet контейнер PetCounter Map заполняется типами из LiteralPetCreator.allTypes. При этом используется класс net.mindview. util.MapData, который получает Iterable (allTypesList) и константу (0 в данном случае) и заполняет Map ключами из allTypes со значениями 0. Без предварительного заполнения Map будут подсчитаны только случайно сгенерированные типы, но не базовые типы (такие, как Pet и Cat).

Как видите, метод islnstance() избавил нас от необходимости нагромождать конструкции с instanceof. Вдобавок теперь в программу можно легко добавить новые типы Pet — для этого следует просто изменить массив LiteralPet Creator, types; остальная часть программы не потребует правки (которая была бы неизбежна с операторами instanceof).

Метод toStringO был перегружен для получения удобочитаемого вывода.

Рекурсивный подсчет

Контейнер Map в PetCount3.PetCounter был заполнен всеми классами Pet. Вместо предварительного заполнения карты мы также можем воспользоваться методом Class.isAssignableFrom() и создать обобщенный инструмент подсчета, не ограниченный подсчетом Pet:

//: net/mi ndvi ew/uti1/TypeCounter.java // Подсчет экземпляров в семействе типов package net.mindview.util;

import java.util.*;

public class TypeCounter extends HashMap<Class<?>,Integer>{ private Class<?> baseType; public TypeCounter(CIass<?> baseType) { this.baseType = baseType;

}

public void count(Object obj) {

Class<?> type = obj .getClassO; i f(!baseType.i sAssi gnableFrom(type))

throw new RuntimeException(obj + " incorrect type: " + type + should be type or subtype of " + baseType);

countClass(type);

}

private void countClass(Class<?> type) { Integer quantity = get(type); put(type, quantity == null ? 1 : quantity +1); Class<?> superclass = type.getSuperclassO; if(superClass != null &&

baseType.i sAssi gnableFrom(superClass)) countClass(superClass);

}

public String toStringO {

StringBuilder result = new StringBuilder("{"); for(Map.Entry<Class<?>.Integer> pair : entrySetO) { result.append(pair.getKeyО.getSimpleName()); result.append("="); res ul t.a ppend(pa i r.getVa1ue О); result.append(". ");

}

result.delete(result.1ength О -2, result. 1 ength О); result.append("}"); return result.toStringO;

}

} ///:-

Метод count() получает Class для своего аргумента, а затем использует.isAs-signableFrom() для проверки принадлежности объекта к интересующей вас иерархии. Метод countClas^O сначала производит подсчет для точного типа класса, а затем, если baseType допускает присваивание из суперкласса, рекурсивно вызывает countClass() для суперкласса.

II: typeinfo/PetCount4.java

import typeinfo.pets.*,

import net.mindview.util.*;

import static net.mindview.util.Print.*;

public class PetCount4 {

public static void main(String[] args) {

TypeCounter counter = new TypeCounter(Pet.class); for(Pet pet : Pets.createArray(20)) {

printnb(pet.getClass().getSimpleNameO + " "); counter.count(pet);

}

printO:

print(counter); _ Л

продолжение &

}

} /* Output: (Пример)

Rat Manx Cymric Mutt Pug Cymric Pug Manx Cymric Rat EgyptianMau Hamster EgyptianMau Mutt Mutt Cymric Mouse Pug Mouse Cymric

{Mouse=2, Dog=6, Manx=7, EgyptianMau=2, Rodent=5, Pug=3, Mutt=3. Cymric=5, Cat=9. Hamster=l, Pet=20, Rat=2} *///:-

Как видно из результатов, подсчитываются как базовые, так и конкретные типы.

Регистрация фабрик

У построения объектов иерархии Pet есть один недостаток: каждый раз, когда в иерархию включается новый тип Pet, вы должны добавить его в LiteralPet-Creator.java. В системах с регулярным добавлением новых классов это может создать проблемы.

Первое, что приходит в голову, — добавить в каждый класс статический инициализатор, который добавлял бы свой класс в некий список. К сожалению, статические инициализаторы вызываются только при первой загрузке класса, поэтому возникает «порочный круг»: класс отсутствует в списке генератора, поэтому генератор не может создать объект этого класса, соответственно, класс не загрузится и не будет помещен в список.

1 ... 73 74 75 76 77 78 79 80 81 ... 132
На этой странице вы можете бесплатно читать книгу Философия Java3 - Брюс Эккель бесплатно.
Похожие на Философия Java3 - Брюс Эккель книги

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