Замена 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);
}
}
2 ответа
Как вы уже обнаружили, 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
конструктор, создай новыйException
, но не бросай это. - Временно поменять местами
System.err
для вашего собственного писателя, который пишет вByteArrayOutputStream
- Вызов
printStackTrace()
на исключение - Восстановить
System.err
к первоначальной стоимости - Разобрать трассировку стека из
ByteArrayOutputStream
, Имена конструкторов промежуточных классов будут в трассировке стека. Теперь вы можете посмотреть их, используяClass.forName()
и добавить их в дерево.
У вас есть два варианта:
Если вы знаете, что суперкласс принадлежит ограниченному набору, вы можете просто вызвать instanceof или использовать метод Class.isInstance().
Кроме того, у вас может быть препроцессор для запуска вашего кода и создания структуры данных, которая сохраняется отдельно, которая содержит информацию о ваших классах. Возможно, даже пользовательский доклет сможет это сделать. Вывод может быть текстовым или двоичным файлом, который описывает структуру:
ClassA:SuperClass
ClassB:AnotherSuperClass
etc.
Обратите внимание, что у вас могут быть проблемы с запутыванием таким образом.