2017-03-10 3 views
0

おそらく隠された質問は、階層構造を持つキーにどの構造を使用するかということでしょう(クラスと内部クラス特定のサブセットに対するテストが可能であること)。適切な場所に新しいキーを追加し、適切なkeySetでこのキーを自動で使用できる構造を探しています。ここで私の実際の試行: 私は静的な最終的な文字列と対応するkeySetとしてキーで動作します。 あるキーが他のクラスで宣言されているキーのセット(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; 
    }  
} 

今私はキーセットもタイプ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(); 
       } 
      } 
     } 
    } 
} 
+3

"よくテストする必要があります..."多分、それが何か起きているのであれば、あなたのデザインに取り組むべきでしょうか? – Kayaman

+0

ああ... [XY問題](http://xyproblem.info/)の刺激的な香り – shmosel

+0

'f.get(f)'は明らかに間違っています。あなたは 'java.lang.reflect.Field'クラスのフィールドを読んでいません。したがって、静的フィールドでない場合は 'static'フィールドで 'f.get(null)'を使用するか、 'f.get(this) 'を使用するかをチェックします。 – Holger

答えて

0

私が最初に間違っていない場合は、内部クラスのコンストラクタを宣言する必要があります。外部クラスのインスタンスを引数として呼び出します。

+0

私はスタティックファイナルのみを使用しています。それはどんなインスタンスでもアクセス可能でなければならないが、私はそれをどうやって行うのか分からなかった。 Holgerはそれを解決する良いアイデアを持っていました。 –

0

内部クラスを静的にするか、すでに勇気づけられているように、外部クラスのインスタンスを通じてネストされたインスタンスを作成します(Instantiating inner classを参照)。

文字列定数の代わりに列挙型を使用することを検討してください。このソリューションは、ニーズに応じて改善することができる

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

とインターフェースを実装することができます。

+0

隠された質問を解決するためのヒントを教えてくれたと思います(編集のイントロを参照)。申し訳ありませんが、私はqutieは列挙型、列挙型インターフェイスの利点を理解していないとあなたは何をenumグループの一様な処理でmeenですか?興味深いものがあると思われます。さらなる説明を追加する時間があれば嬉しいです。 –

+0

私の編集した回答を参照 –

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メソッドは、スーパークラスフィールドを処理するためにこれを使用するため、異なるサブクラスに対して呼び出すと、その結果を繰り返して、共通基本クラスの結果を共有します。

サブクラスはここで何もする必要はなく、実際のクラスを返すgetClass()を使用するので、継承されたkeySet()メソッドで十分です。

この図に示すように、ideone demo。あなたは、Java 7の前にJavaバージョンで実行されている場合は


、あなたはできるだけ早くあなたが新しい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<String>()
new TreeSet<>()ダイヤモンド演算子の使用を置き換えています。それでは、Java 6で動くはずです。

+0

コードレビューのために何を見ていただき、ありがとうと思いますか? –

+0

私はまだJAVA 6になっています(この夏にはJAVA 8への移行が予定されていますが、JAVA 9が表示された後になるかもしれません:-)) 私はそれまで待ってから提案書を使うか、ClassValue und computeValue ? –

+0

あなたはJava 7をスキップしていますか?なぜですか?とにかく、夏まで使用できるClassValueのスタンドを追加しました。 – Holger

関連する問題