У меня интересная ситуация, и мне интересно, есть ли лучший способ сделать это. Ситуация такова: у меня есть древовидная структура (абстрактное синтаксическое дерево, в частности), а некоторые узлы могут содержать дочерние узлы разных типов, но все они расширены из заданного базового класса.
Я хочу часто делать запросы по этому дереву, и я хотел бы получить обратно определенные подтипы, которые меня интересуют. Поэтому я создал класс предикатов, который затем могу передать в общий метод запросов. Сначала у меня был метод запроса, который выглядел так:
public <T extends Element> List<T> findAll(IElementPredicate pred, Class<T> c);
где аргумент Class
использовался только для указания типа возврата. Меня беспокоило то, что все мои предикаты были уже для определенных типов, поэтому здесь имеется избыточная информация. Типичный вызов может выглядеть так:
List<Declaration> decls =
scope.findAll(new DeclarationPredicate(), Declaration.class);
Поэтому я реорганизовал его следующим образом:
public <T extends Element> List<T> findAll(IElementPredicate<T> pred);
Где интерфейс IElementPredicate
выглядит следующим образом:
public interface IElementPredicate<T extends Element> {
public boolean match(T e);
public String getDescription();
public Class<T> getGenericClass();
}
Дело в том, что интерфейс предиката расширяется, чтобы вместо этого предоставить объект Class
. Это заставляет писать фактический findAll
метод немного больше работы, и он добавляет немного больше работы по написанию предиката, но это оба по сути крошечные "одноразовые" вещи, и это делает запрос намного приятнее, потому что вы надеваете 't нужно добавить дополнительный (потенциально избыточный) аргумент, например
List<Declaration> decls = scope.findAll(new DeclarationPredicate());
Я раньше этого не заметил. Является ли это типичным способом работы с семантикой Java-дженериков? Просто любопытно, если мне не хватает лучшего образца.
Commments?
UPDATE:
Вопрос в том, для чего нужен класс? Вот реализация findAll:
public <T extends Element> List<T> findAll(IElementPredicate<T> pred) {
List<T> ret = new LinkedList<T>();
Class<T> c = pred.getGenericClass();
for(Element e: elements) {
if (!c.isInstance(e)) continue;
T obj = c.cast(e);
if (pred.match(obj)) {
ret.add(c.cast(e));
}
}
return ret;
}
Хотя верно, что соответствие принимает только T, мне нужно убедиться, что объект является T, прежде чем я могу его вызвать. Для этого мне нужны методы "isInstance" и "cast" класса (насколько я могу судить).