Чтение онлайн

ЖАНРЫ

Философия Java3

Эккель Брюс

Шрифт:

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. В системах с регулярным добавлением новых классов это может создать проблемы.

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

По сути, вы вынуждены создать список вручную (разве что вы напишете утилиту, которая будет анализировать исходный код, а затем создавать и компилировать список). Вероятно, лучшее, что можно сделать, — это разместить список в одном централизованном, очевидном месте. Вероятно, лучшим местом для него будет базовый класс иерархии.

В этом разделе мы также внесем другое изменение: создание объекта будет передано самому классу с использованием паттерна «метод-фабрика». Метод-фабрика может вызываться полиморфно и создает объект соответствующего типа. В следующей упрощенной версии методом-фабрикой является метод create интерфейса Factory:

//: typeinfo/factory/Factory.java package typeinfo.factory:

public interface Factory<T> { T createO; } ///•-

Обобщенный параметр T позволяет create возвращать разные типы для разных реализаций Factory. Также при этом используется ковариантность возвращаемых типов.

Поделиться с друзьями: