2016-03-25 4 views
1

私は過去数時間、次の機能が動作するように努力してきました。Android enum reflection

public class OMG { 

    String name; 
    HashMap<SETTINGS, String> settings; 

    public enum SETTINGS { 
     Setting1("OMG setting 1"), Setting2("OMG setting 2"); 
     private String key; 

     @Override 
     public String toString() { 
      return "SETTINGS: " + key; 
     } 

     SETTINGS(String key){ 
      this.key = key; 
     } 
    } 

    public OMG(String name, HashMap<SETTINGS, String> settings) { 
     this.name = name; 
     this.settings = settings; 
    } 
} 

public class Test { 

    public static void main(String[] args) { 
     try { 
      Class<?> c = Class.forName("path.to.OMG" + "$" + "SETTINGS"); 

      System.out.println(Arrays.toString(c.getEnumConstants())); 
      HashMap<c,String > values = new HashMap<>(); 
      OMG omg = new OMG("blah",values); 

     } catch (ClassNotFoundException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

私は、OMGのインスタンスを作成する必要がありますとOMGへのパスと列挙NAMEを与えられています。これまでのところ、私はTestクラスのコードを持っています。私はすべてのenumValuesを得ることができますが、私はHashMapを作成する方法と、このHashMapを使ってコンストラクタを呼び出してOMGクラスのインスタンスを取得する方法がわかりません。

助けてください。

ありがとうございました

+0

本当にあなたはenum名で作業する必要がありますか?なぜあなたは 'HashMap 'を作成できないのですか?また、 'OMG'クラスは任意の列挙型で動作するか、単に' SETTINGS'列挙型で動作する必要がありますか? –

+0

実際にOMGクラスには複数の列挙型があり、複数の辞書を渡す必要があります – mp3por

+0

問題は解決しましたか?そうでない場合は、コード例と問題の説明を投稿してください。そうであれば、最も役に立つ回答を受け入れることでこれを示してください。 –

答えて

2

あなたがしようとしていることを行う現実的な理由はありません。 OMGをインスタンス化する方法はしかし、純粋に学術的な運動(私はこれらが好き!)として、我々は唯一の文字列として指定されたクラス名を使用してOMGをインスタンス化し、実際にOMGまたはSETTINGSを入力しなくてもできるかどうかを見てみましょうnew OMG("foo", new HashMap<SETTINGS, String>());

を行うことですどこでも。それはできます!

Javaでは、ジェネリックはコンパイル時の機能です。実行時にHashMap<SETTINGS, String>は、HashMap<Double, Float>または生のHashMapと変わらない。したがって、古いHashMapをインスタンス化することができます。オブジェクトcをまったく使用する必要はありません(とにかく、Classオブジェクトを型パラメータとして使用することはできません)。ここで

がソリューションです:

public static void main(String[] args) throws Exception { 
    Class<?> clazz = Class.forName("path.to.OMG"); 
    Constructor constructor = clazz.getConstructor(String.class, HashMap.class); 
    Object omgInstance = constructor.newInstance("foo", new HashMap<Void, Void>()); 
    System.out.println(omgInstance.getClass()); // OMG - it worked! 
} 
+0

あなたの第3段落は私の考えを得て、基本的に私の問題を解決しました。ありがとうございました。 – mp3por

+0

私は助けることができてうれしいです。 –

1

あなた自身の答えから、私はあなたがクラスOMGにマップに格納されている実際の設定のためのキーとデフォルト値として列挙エントリを使用することをお読みください。この構成は、ファイルにも書き込む必要があります。

ここで、設計上の決定を下す必要があります。いつどの設定キーを使用できるか、デフォルト値を変更できるようにしたいのですか?プログラムはすでに(実行時に)実行されているか、プログラムが(コンパイル時に)再コンパイルされていますか?

説明は、実行時に変更できるようにすることをお勧めします。これは正しいです?また、これは本当に必要ですか?あなたの既存のコードはおそらく、新しいキーに提供されている値を使用しません。また、変更されたデフォルト値は、おそらく古いデフォルト値を使用して初期化されているため、影響を与えません。

実行時にこれらの情報を本当に変更したい場合は、列挙型を使用してそれらを格納して読み込むべきではありません。このようなプログラミングスタイルはJavaScriptなどのスクリプト言語でも機能しますが、Javaではうまく機能しません。列挙型は他のコードと同様に、実行時には変更されませんが、コンパイル時に指定する必要があります。

実行時にコードが変更されることは想定されていないため、JVMはクラスローダーの魔法をせずに単にクラスをリロードすることさえ許可しません。可能であれば、これを避けたいと考えています。詳細については、hereを参照してください。実行時にクラスをリロードすることは不可能ではありませんが、あなたが直面している問題に対して間違った解決策であることは間違いありません。

正しい解決策は何ですか?実行時に情報を本当に変更する必要があると私はまだ確信していません。これが正しい場合、コンパイル時に情報を変更するだけでよい場合は、現在の列挙型アプローチを使用することができます。ただし、クラスを名前でロードしないようにシンプルにすることができます。実装についての詳細は、以下を参照してください。

実際に実行時に情報を変更する必要がある場合は、情報をコードに保存するのではなく、ファイルに保存してください。これにより、ファイルを読み直して簡単に情報をリロードすることができます。プロパティファイルを使用することができます。これは、Javaワールドでは一般的です。hereを参照してください。より構造化されたファイル形式が必要な場合は、JSONファイルを使用できます。 Javaでは、Gsonを使用してJSONファイルを操作できます(hereを参照)。


これが私の作品:

package test; 

import java.util.Arrays; 
import java.util.EnumMap; 

public class Test { 
    public static enum SETTINGS { 
     Setting1("OMG setting 1"), Setting2("OMG setting 2"); 

     private String key; 

     @Override 
     public String toString() { 
      return "SETTINGS: " + key; 
     } 

     SETTINGS(String key) { 
      this.key = key; 
     } 
    } 

    public static class OMG<E extends Enum<E>> { 
     String name; 
     EnumMap<E, String> settings; 

     public OMG(String name, EnumMap<E, String> settings) { 
      this.name = name; 
      this.settings = settings; 
     } 

     public static <E extends Enum<E>> OMG<E> create(String name, 
       Class<E> enumClass) { 
      return new OMG<E>(name, new EnumMap<E, String>(enumClass)); 
     } 
    } 

    public static void main(String[] args) { 
     try { 
      Class c = Class.forName("test.Test$SETTINGS"); 
      // alternatively you can use: 
      // Class<SETTINGS> c = SETTINGS.class; 

      System.out.println(Arrays.toString(c.getEnumConstants())); 
      OMG.create("foobar", c); 
     } catch (ClassNotFoundException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

私はあなたがクラスOMGだけでなくSETTINGSに任意の列挙型を渡したいと仮定しました。 SETTINGSのみにする必要がある場合は、SETTINGSをコード内で直接使用することもできます。これがSETTINGS列挙型をOMGクラスから移動した理由です。

次に、汎用タイプのパラメータEをクラスOMGに追加しました。したがって、OMGSETTINGSに直接依存しません。簡単な初期化を可能にするために、static factoryメソッドをOMGに追加しました。このメソッドはジェネリック型パラメータEも使用しています。 Eと一致する名前とクラスオブジェクトを受信するだけです。 Javaでは、ジェネリック型のパラメータを直接反映させることができないため、クラスオブジェクトが必要です。

また、EnumMapは、キーが列挙型の場合に最適化されたマップ実装であるため、をEnumMapに置き換えました。

最後に、OMGクラスの初期化:リフレクションを使用して、クラス名からクラスオブジェクトを実際に作成する必要がある場合は、そのコードをそのまま使用できます。これにより、警告が生成されることに注意してください。しかし、ほとんどの場合、コメント行のコードのように、クラスオブジェクトを直接使用することが必要です。この2番目の方法を使用すると、警告が表示されず、try-catchブロックを省略することもできます。

0

@PaulBoddingtonのおかげで、私は次のように問題を解決しました。

public class Test { 

    public static void main(String[] args) { 
     try { 
      Class<?> c = Class.forName("path.to.OMG" + "$" + "SETTINGS"); 
      Object[] enumConstants = c.getEnumConstants(); 
      HashMap<Object, String > configuration = new HashMap<>(); 
      for (Object enumConstant: enumConstants){ 
       configuration.put(enumConstant, enumConstant.key); 
      } 
      OMG omg = new OMG("blah", configuration); 

     } catch (ClassNotFoundException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

疑問人々のためのユースケースは、OMGが設定可能であり、また容易に追加するビュー新しい設定の開発者の観点から可能にするためにあるシステム内のモジュールであることです。これは、設定とその値がHashMap < SETTING、STRING>に保持されている理由であり、SETTINGは設定が見つかるはずの設定ファイル内のキーとその他の情報(例のデフォルト値)を含むenumです。 OMGクラスには、設定値を返す、モジュールによって公開されているものもあり、

String getSettingValue(SETTING setting){ 
    return settingsMap.get(setting); 
} 

というメソッドがあります。

これは、新しい設定をコンポーネントに追加する必要がある場合は、SETTINGS列挙型の新しいインスタンスを作成するだけで、それを実現する必要があることを意味します。作成中にサーバーがこの設定の値を送信していない場合は、デフォルトまたはクラッシュを使用できます。一方、コンポーネントがインスタンス化されると、既存のコードに触れることなく新しい設定に即座にアクセスできます。

学校のプロジェクトのためにこのアーキテクチャーに意見をお寄せください。教師が実際にそれを調べて合理的であるかどうかを教えてくれるのは間違いです。

ありがとうございます

+0

私はこの答えの情報を反映するために私の答えを拡張しました。ここでのほんの少しの発言:この答えのコードはコンパイルされていますか? 'EnumConstant.key'は有効ではありません。なぜなら' Object'は '.key'を定義していないからです。次に、実行時に新しい列挙型エントリを作成することはできません。また、列挙型を簡単にリロードすることはできません。列挙型を使用して情報を格納およびロードしています。実行時に情報を変更したくない限り、それは問題ありません。詳細は、私の答えをご覧ください。 –