0

也许隐藏的问题是对于具有某种层次结构的键使用哪种结构(因此我尝试使用类和内部类,以便可以对特定子集进行测试)。我正在寻找一种结构,我可以在其中将新密钥添加到适当的位置,并在适当的 keySet 中自动拥有该密钥。这是我的实际尝试:现在我将键作为静态最终字符串和相应的键集。我经常需要测试某个键是否包含在其他类中声明的键集(公共静态最终字符串)中。因此,我使用 Keys1 类中的键扩展所有类,该类 Keys1 具有提供键集的方法 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();
                }
            }
        }
    }
}
4

3 回答 3

0

使您的内部类静态或正如已经提到的 briarheart 通过外部类的实例创建嵌套实例(请参阅实例化内部类)。

考虑使用枚举而不是字符串常量。

你可以使用类似的东西:

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);

为每个枚举检查在任何枚举及其嵌套枚举中是否包含任意值。

于 2017-03-10T10:11:48.670 回答
0

既然你说,你只是在寻找public static final String领域,你在做不必要的工作。您不会过滤字段以static仅访问字段,此外,您正在查询字段并检查结果的类型,而不是首先检查字段的类型。

此外,您不需要对象实例来检索static字段。如果您以在 a 上操作的方式编写代码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(),它返回实际的类。

如this ideone demo所示。


当您在 Java 7 之前的 Java 版本中运行时,您可以使用以下 ersatz,一旦您迁移到更新的 Java 版本,您应该将其替换为真实的东西。

/**
 * TODO: replace with {@code java.lang.ClassValue<T>} when migrating to &gt;=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<>()为显式键入的new TreeSet<String>(). 然后,它应该可以在 Java 6 中运行。

于 2017-03-10T15:56:55.193 回答
0

如果我一开始没记错的话,你需要获得内部类的声明构造函数。比使用外部类的实例作为参数调用它。

于 2017-03-10T10:06:45.803 回答