也许隐藏的问题是哪个结构用于具有某种层次结构的键(因此我尝试使用类和内部类,以便可以对特定子集进行测试)。我正在寻找一种结构,我可以在适当的位置添加新密钥,并在相应的密钥集中自动使用此密钥。在这里我的实际尝试: 现在我使用key作为静态final String和相应的keySet。 我经常需要测试某个键是否包含在某个其他类中声明的键集(public static final String)中。 因此,我使用Keys1类中的键扩展所有类,其中key1具有提供键集的方法keySet()。这很好。
public class Keys1
{
private TreeSet<String> m_keySet = new TreeSet<String>();
public Keys1()
{
initKeySet();
}
private void initKeySet()
{
Field[] felder = this.getClass().getFields();
for (Field f : felder)
{
if (Modifier.isFinal(f.getModifiers()))
{
try
{
if (f.get(f) instanceof String)
{
m_keySet.add(f.get(f).toString());
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
}
public TreeSet<String> keySet()
{
return m_keySet;
}
}
现在我徒劳地尝试在类Keys2中编写类似的功能,其中keySet还应该包含在Keys2类型的内部类中声明的键。
public class Keys2 extends Keys1
{
@Override
protected void initKeySet()
{
super.initKeySet();
Class<?>[] innerClasses = this.getClass().getDeclaredClasses();
for (Class<?> innerClass : innerClasses )
{
if (innerClass.getClass().isInstance(Keys1.class))
{
Keys1 newKeys;
try
{
newKeys = (Keys1) innerClass.newInstance(); // Doesn't work
keySet().addAll(newKeys.keySet());
}
catch (InstantiationException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
}
}
}
}
答案 0 :(得分:0)
如果我一开始没有弄错,你需要获得内部类的声明构造函数。比调用外层类的实例作为参数。
答案 1 :(得分:0)
使你的内部类静态或者已经提到briarheart通过外部类的实例创建嵌套实例(参见Instantiating inner class)。
考虑使用枚举而不是String常量。
您可以使用以下内容:
public enum A {
A1,
A2;
public static enum B {
B1,
B2
}
public static enum C {
C1,
C2
}
static Set<Enum> allValues() {
Set<Enum> allValues = new HashSet<>();
allValues.addAll(Arrays.asList(A.values()));
allValues.addAll(Arrays.asList(A.B.values()));
allValues.addAll(Arrays.asList(A.C.values()));
return allValues;
}
}
根据您的需要,可以改进此解决方案。
例如,您可以使用方法
实现接口boolean contains(Enum e);
为每个枚举检查在任何枚举中包含任意值 及其嵌套的枚举。
答案 2 :(得分:0)
既然你说过,你只是在寻找public static final String
字段,那你就是在做不必要的工作。您没有过滤字段只能访问static
字段,而且,您正在查询字段并检查结果的类型,而不是首先检查字段的类型。
此外,您不需要对象实例来检索static
字段。如果以对Class
运行的方式编写代码,它可以用于处理内部类,而不是实例化它们。
由于此过程不需要对象实例,因此也没有理由为每个实例重复该操作,也不将结果存储在实例字段中。你只需要记住每个类的结果,谢天谢地,有一个名为ClassValue
的类可以免费提供。
将它们放在一起,您可以将其实现为
public class Keys1 {
static final ClassValue<TreeSet<String>> KEYS = new ClassValue<TreeSet<String>>() {
@Override protected TreeSet<String> computeValue(Class<?> type) {
final int desired=Modifier.PUBLIC|Modifier.STATIC|Modifier.FINAL;
Field[] fields=type.getDeclaredFields();
TreeSet<String> set = new TreeSet<>();
for(Field f: fields) {
if((f.getModifiers()&desired)==desired && f.getType()==String.class) try {
set.add((String)f.get(null));
} catch(IllegalAccessException ex) {
throw new AssertionError(ex);
}
}
for(Class<?> inner: type.getDeclaredClasses()) {
set.addAll(get(inner));
}
type = type.getSuperclass();
if(type != null && type != Object.class) set.addAll(get(type));
return set;
}
};
public TreeSet<String> keySet() {
return KEYS.get(getClass());
}
}
ClassValue
负责缓存。当您调用get
时,它会检查指定类是否已有计算值,否则调用computeValue
。此解决方案中的computeValue
方法利用它本身来处理超类字段,因此如果您为不同的子类调用它,它们将共享公共基类的结果,而不是重复工作。
子类不需要在这里做任何事情,继承的keySet()
方法就足够了,因为它使用getClass()
,它返回实际的类。
如此ideone demo所示。
当您在Java 7之前运行Java版本时,您可以使用以下ersatz,一旦迁移到较新的Java版本,就应该将其替换为真实版本。
/**
* TODO: replace with {@code java.lang.ClassValue<T>} when migrating to >=7.
*/
abstract class ClassValue<T> {
private final ConcurrentHashMap<Class<?>,T> cache=new ConcurrentHashMap<Class<?>,T>();
protected abstract T computeValue(Class<?> type);
public final T get(Class<?> key) {
T previous = cache.get(key);
if(previous != null) return previous;
T computed = computeValue(key);
previous = cache.putIfAbsent(key, computed);
return previous!=null? previous: computed;
}
}
解决方案本身所需的唯一变化是取代钻石操作员使用
明确键入new TreeSet<>()
的{{1}}。然后,它应该在Java 6中工作。