0

おそらく、隠れた問題は、ある種の階層を持つキーにどの構造を使用するかということです (したがって、特定のサブセットのテストが可能になるように、クラスと内部クラスを使用しようとしています)。新しいキーを適切な場所に追加し、このキーを適切なキーセットに自動的に配置できる構造を探しています。ここで私の実際の試み:今、静的な最終的な文字列と対応するキーセットとしてキーを操作します。特定のキーが他のクラスで宣言されたキーのセット (public static final String) に含まれているかどうかをテストする必要があることがよくあります。したがって、キーのセットを提供するメソッド keySet() を持つクラス Keys1 のキーを使用してすべてのクラスを拡張します。それはうまくいきます。

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

ここで、KeySet に Keys2 型の内部クラスで宣言されたキーも含める必要がある 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

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 &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 に答える