Замена Class.getSuperclass() на Java ME?
Вопрос
Как я могу получить суперкласс экземпляра класса в Java ME.То есть подделать функциональность Class.getSuperclass() с ограниченной функциональностью, доступной в CLDC 1.1?
Что я хочу сделать, так это позволить абстрактному суперклассу делать что-то вроде этого:
public Styler getStylerForViewClass(Class clazz) {
Styler s = stylers.get(clazz);
if (s == null) {
for (Class c = clazz; s == null; c = c.getSuperclass()) {
if (c == Object.class) {
throw new IllegalArgumentException("No Styler for " + clazz.getName());
}
s = createStylerForViewClass(c);
}
stylers.put(clazz, s);
}
return s;
}
public Styler createStylerForViewClass(Clazz clazz) {
if (clazz == View.class) {
return new DefaultStyler();
} else {
return null;
}
}
Затем подклассы могли бы добавлять специализации, подобные этой:
public Styler createStylerForViewClass(Class clazz) {
if (clazz == SpecialView.class) {
return new SpecialStyler();
} else {
return super.createSylerForViewClass(clazz);
}
}
Решение
Как вы уже обнаружили, MIDP не предоставляет метода для получения суперкласса класса или для перечисления всех классов в приложении.
Таким образом, все, что вы можете сделать, это самостоятельно отслеживать иерархию классов.
Наличие общего суперкласса немного упрощает задачу, поскольку вы можете заставить новый объект добавить свой собственный класс в глобальную коллекцию классов (если он еще не присутствует) в конструкторе суперкласса:
abstract class View {
protected View() {
classHierarchy.add(this.getClass());
}
}
но, к сожалению, это не будет работать для абстрактных классов, потому что экземпляры никогда не создаются.
Отслеживать отношения суперкласса / подкласса для известного подмножества классов достаточно просто.например ,:
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
public class ClassHierarchy {
public ClassHierarchy() {
childToParentMap = new Hashtable();
parentToChildMap = new Hashtable();
parentToChildMap.put(Object.class, new Vector());
}
public boolean addClass(Class toAdd) {
if (toAdd.isInterface()) return false;
if (toAdd.equals(Object.class)) return false;
if (childToParentMap.get(toAdd) != null) return false;
addClassBelow(toAdd, Object.class, new Vector());
return true;
}
public Class getParent(Class subclass) {
return (Class) childToParentMap.get(subclass);
}
private void addClassBelow(Class toAdd, Class parent, Vector initialChildren) {
Vector children = (Vector) parentToChildMap.get(parent);
Class reparented;
do {
reparented = null;
for (Enumeration childEnum = children.elements();
childEnum.hasMoreElements();
) {
Class child = (Class) childEnum.nextElement();
if (child.isAssignableFrom(toAdd)) {
addClassBelow(toAdd, child, initialChildren);
return;
} else if (toAdd.isAssignableFrom(child)) {
children.removeElement(child);
initialChildren.addElement(child);
childToParentMap.put(child, toAdd);
// Guard against concurrent modification
reparented = child;
break;
}
}
} while (reparented != null);
children.addElement(toAdd);
childToParentMap.put(toAdd, parent);
parentToChildMap.put(toAdd, initialChildren);
}
private Hashtable childToParentMap;
private Hashtable parentToChildMap;
}
Но это может "пропустить" промежуточные классы, которые будут добавлены позже, напримересли у вас есть эти классы:
Object >= View >= A >= B >= C
и добавить A
и C
к дереву и запросил у него суперкласс C
это дало бы вам A
, и если вы позже добавите B
это заменило бы A
как суперкласс C
, но только до тех пор , пока не был возвращен неправильный стилизатор для некоторых экземпляров C
.
Поэтому я думаю, вам придется добавить ограничение, согласно которому классы-предки (для которых определены стилеры) должны быть сначала добавлены в дерево.Возможно, из блока статического инициализатора класса, который переопределяет createStylerForViewClass
, или статический инициализатор самого класса view.
Я действительно думал об одном другом злом взломе, но я не могу действительно рекомендовать его:
- В
View
конструктор, создайте новыйException
, но не бросайте его. - Временно поменяться местами
System.err
для вашего собственного автора, который пишет вByteArrayOutputStream
- Позвонить
printStackTrace()
об исключении - Восстановить
System.err
к своему первоначальному значению - Проанализируйте трассировку стека из
ByteArrayOutputStream
.Имена конструкторов промежуточных классов будут указаны в трассировке стека.Теперь вы можете просмотреть их с помощьюClass.forName()
и добавьте их в дерево.
Другие советы
У вас есть два варианта:
Если вы знаете, что суперкласс принадлежит к ограниченному набору, вы можете просто вызвать instanceof или использовать Класс.isInstance() способ.
В качестве альтернативы, вы можете использовать препроцессор для запуска вашего кода и создания структуры данных, которая сохраняется отдельно и содержит информацию о ваших классах.Возможно, даже обычай доклет могу это сделать.Результатом может быть текстовый или двоичный файл, описывающий структуру:
ClassA:SuperClass
ClassB:AnotherSuperClass
etc.
Обратите внимание, что у вас могут возникнуть проблемы с запутыванием таким образом.