Мне удалось найти решение, использующее рефлексию, которая, кажется, работает - я не прошел через весь спектр тестирования, я просто дурачился, глядя на возможные варианты.
ActiveField: Java-класс, который будет расширяться всеми остальными классами «Fields» (которые будут внутренними классами в моих классах Model). У этого есть нестатический метод "getKeys ()", который просматривает класс "this's" и извлекает из него список всех полей. Затем он проверяет несколько вещей, таких как модификаторы, тип поля и регистр, чтобы убедиться, что он смотрит только на поля, соответствующие моему соглашению: все «ключи поля» должны быть «public static final» типа String, а имя поля должно быть все ВЕРХНИЙ.
public class ActiveField {
private final String key;
protected ActiveField() {
this.key = null;
}
public ActiveField(String key) {
System.out.println(key);
if (key == null) {
this.key = "key:unknown";
} else {
this.key = key;
}
}
public String toString() {
return this.key;
}
@SuppressWarnings("unchecked")
public List<String> getKeys() {
ArrayList<String> keys = new ArrayList<String>();
ArrayList<String> names = new ArrayList<String>();
Class cls;
try {
cls = Class.forName(this.getClass().getName());
} catch (ClassNotFoundException e) {
return keys;
}
Field fieldList[] = cls.getFields();
for (Field fld : fieldList) {
int mod = fld.getModifiers();
// Only look at public static final fields
if(!Modifier.isPublic(mod) || !Modifier.isStatic(mod) || !Modifier.isFinal(mod)) {
continue;
}
// Only look at String fields
if(!String.class.equals(fld.getType())) {
continue;
}
// Only look at upper case fields
if(!fld.getName().toUpperCase().equals(fld.getName())) {
continue;
}
// Get the value of the field
String value = null;
try {
value = StringUtils.stripToNull((String) fld.get(this));
} catch (IllegalArgumentException e) {
continue;
} catch (IllegalAccessException e) {
continue;
}
// Do not add duplicate or null keys, or previously added named fields
if(value == null || names.contains(fld.getName()) || keys.contains(value)) {
continue;
}
// Success! Add key to key list
keys.add(value);
// Add field named to process field names list
names.add(fld.getName());
}
return keys;
}
public int size() {
return getKeys().size();
}
}
Затем в моих классах "Модель" (которые являются модными обертками вокруг Карты, которые можно проиндексировать с помощью полей Fields)
открытый класс ActiveResource {
/ **
* Базовые поля для моделирования ActiveResource objs - Все классы, которые наследуются от
* ActiveResource должен иметь эти поля / значения (если не переопределено)
* /
открытый статический класс Fields extends ActiveField {
public static final String CREATED_AT = "узел: создан";
public static final String LAST_MODIFIED_AT = "node: lastModified";
}
public static final Fields Fields = new Fields();
... other model specific stuff ...
}
Затем я могу создать класс Foo, который расширяет мой класс ActiveResource
public class Foo extends ActiveResource {
public static class Fields extends ActiveResource.Fields {
public static final String FILE_REFERENCE = "fileReference";
public static final String TYPE = "type";
}
public static final Fields Fields = new Fields();
... other Foo specific stuff ...
Теперь я могу сделать следующее:
ActiveResource ar = new ActiveResource().
Foo foo = new Foo();
ar.Fields.size() #=> 2
foo.Fields.size() #=> 4
ar.Fields.getKeys() #=> ["fileReference", "type", "node:created", "node:lastModified"]
foo.Fields.getKeys() #=> ["node:created", "node:lastModified"]
ar.Fields.CREATED_AT #=> "node:created"
foo.Fields.CREATED_AT #=> "node:created"
foo.Fields.TYPE #=> "type"
etc.
Я также могу получить доступ к полям как к статическим полям из моих объектов Model
Foo.Fields.size(); Foo.Fields.getKeys(); Foo.Fields.CREATED_AT; Foo.Fields.FILE_REFERENCE;
Пока это выглядит как довольно хорошее решение, которое потребует минимальных инструкций для создания новых моделей.