2011-02-08 2 views
2

リフレクションによってEnum値を取得するプログラム(Enumは許可されています)があり、その型パラメータとしてEnumをとる汎用クラスでラップすることで何かしたい。しかし、コンストラクタを正しく呼び出す方法がわかりません。私はそれが動作するようにすることができる唯一の方法は、生のタイプを使用することです。リフレクションで得られた列挙定数を使用しています

説明:実際のプログラムは複雑で、実行時にユーザー提供のファイルからenumクラス名を参照します。実際のプログラムのラッパークラスには、enumで実現できない追加の状態とメソッドがあります。ただ学術ためにこれをやっていないメートル。私はそれが不自然に見えることがあります。この問題を説明するために以下の例のプログラムを書いたが、例示的な目的のためにすることになっています。)

誰も私がライン

を修正するのに役立つことができます
EnumWrapper<?> ewrapped = new EnumWrapper(e); 

以下のように警告が少なくなりますか?

プログラムは期待どおりに動作します(見つからない列挙型の例外は3つありますが、それ以外の場合はラップされた列挙型のリストを出力します)。しかし、生の型を決して使用しないで、この場合を修正する。

import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.List; 

public class GenericEnum2 { 

    enum Bird { OWL, EAGLE, HAWK }; 
    enum Mammal { LION, TIGER, BEAR }; 

    static class EnumWrapper<E extends Enum<E>> 
    { 
     final private E value; 

     public EnumWrapper(E value) { this.value = value; } 
     public E getEnum() { return this.value; } 
     @Override public String toString() { return "wrapped "+value.toString(); } 

     static public <E extends Enum<E>> EnumWrapper<E> wrap(E e) { 
      return new EnumWrapper<E>(e); 
     } 
    } 

    public static void main(String[] args) { 
     List<EnumWrapper<?>> list = new ArrayList<EnumWrapper<?>>(); 
     list.add(EnumWrapper.wrap(Bird.OWL)); 
     list.add(EnumWrapper.wrap(Bird.EAGLE)); 
     list.add(EnumWrapper.wrap(Bird.HAWK)); 
     list.add(EnumWrapper.wrap(Mammal.LION)); 
     list.add(EnumWrapper.wrap(Mammal.TIGER)); 
     list.add(EnumWrapper.wrap(Mammal.BEAR));   
     System.out.println(list); 

     list.clear(); 
     for (String s : Arrays.asList(
       "Bird.OWL", 
       "Bird.HAWK", 
       "Bird.FULVOUS_WHISTLING_DUCK", 
       "Mammal.LION", 
       "Mammal.BEAR", 
       "Mammal.WARTHOG", 
       "Computer.COMMODORE_64" 
       )) 
     { 
      String className = GenericEnum2.class.getCanonicalName()+"$"+s; 
      try 
      { 
       Enum<?> e = getEnum(className); 

       // EnumWrapper<?> ewrapped0 = EnumWrapper.wrap(e); 
       /* 
       * Bound mismatch: The generic method wrap(E) of type 
       * GenericEnum2.EnumWrapper<E> is not applicable for 
       * the arguments (Enum<capture#2-of ?>). The inferred 
       * type Enum<capture#2-of ?> is not a valid substitute for 
       * the bounded parameter <E extends Enum<E>> 
       */ 

       // EnumWrapper<?> ewrapped0 = new EnumWrapper<?>(e); 
       // Cannot instantiate the type GenericEnum2.EnumWrapper<?> 

       EnumWrapper<?> ewrapped = new EnumWrapper(e); 
       // this works but gives me the warning of "EnumWrapper" being a raw type 

       list.add(ewrapped); 
      } 
      catch (IllegalArgumentException e) 
      { 
       e.printStackTrace(); 
      } 
      catch (ClassNotFoundException e) { 
       e.printStackTrace(); 
      } 
     } 
     System.out.println(list); 
    } 

    static public Enum<?> getEnum(String enumFullName) throws IllegalArgumentException, ClassNotFoundException 
    { 
     String[] x = enumFullName.split("\\.(?=[^\\.]+$)"); 
     if (x.length == 2) 
     { 
      String enumClassName = x[0]; 
      String enumName = x[1]; 
      @SuppressWarnings("unchecked") 
      final Class<Enum> cl = (Class<Enum>)Class.forName(enumClassName); 
      if (cl.isEnum()) 
      { 
       @SuppressWarnings("unchecked") 
       final Enum result = Enum.valueOf(cl, enumName); 
       return result; 
      } 
      else 
       throw new IllegalArgumentException("Class is not an enum: "+enumClassName); 
     } 
     return null; 
    } 
} 

編集:更新getEnum()OrangeDogの提案あたり:

static public Enum<?> getEnum(String enumFullName) throws IllegalArgumentException, ClassNotFoundException 
    { 
     String[] x = enumFullName.split("\\.(?=[^\\.]+$)"); 
     if (x.length == 2) 
     { 
      String enumClassName = x[0]; 
      String enumName = x[1]; 
      final Class<?> cl = Class.forName(enumClassName); 
      if (cl.isEnum()) 
      { 
       for (Object o : cl.getEnumConstants()) 
       { 
        Enum<?> e = (Enum<?>)o; 
        if (enumName.equals(e.name())) 
         return e; 
       } 
      } 
      else 
       throw new IllegalArgumentException("Class is not an enum: "+enumClassName); 
     } 
     return null; 
    } 
} 
+2

この例はかなり複雑です。コードをもっと簡単にしてラッパーを削除することをお勧めします。 –

+0

合意しましたが、静的なファクトリメソッドを除いては便利なメソッドはありません。 – maaartinus

+1

これは 'Arrays.asList(Bird.OWL、Bird.EAGLE、Bird.HAWK、Mammal.LION、Mammal.TIGER、Mammal.BEAR)' –

答えて

4

がジェネリック医薬品とダイナミクスを混合することは、通常、すべて構文的にタイプセーフを作るための方法でないとき。

通常は、1回の使用を@SuppressWarnings("unchecked")または@SuppressWarnings("rawtypes")に減らすことができます。これは、昔ながらの論理的推論を使用して正しいことが分かります。あなたがしなければならない決定は、「安全でない」操作を行う場所です。

詳細については、Class<Enum>が間違っています。 Class<? extends Enum>isEnum()のチェックをとっているので、Class<?>を使用します)。

また、Class<?>.getEnumConstants()を使用して、より安全に型を再構成できます。正しい名前を探したり、序数を使って直接索引をつけたりしてください。

+0

「具体的には、クラスは間違っています」...ああ、私はここでその質問をしました:http://stackoverflow.com/questions/4750965/java-reflection-to-obtain-an-enum最良の選択肢となる。 –

+0

+1はsuppresswarningsのコメントです... "rawtypes"抑止は "unchecked"よりも良くないか悪いですか? –

+0

re:getEnumConstants():Enum.valueOf()は実際にどのように動作しますか?それはリストを介して実行されますか、またはそれはHashMap型のルックアップを持っていますか? –

1

enum-objectをラップするには、wrap-methodのようにenum-objectの実際の型が必要です(これは型パラメータとして十分です)。しかし、このためにコンパイラは型を推測する必要があり、この場合は不可能であるようです。

問題は、Enum<E>の各インスタンスはタイプEの事実であるが、Enum<E>がコンパイラのEに実際に互換性がないことです。これを以下では<E extends Enum><E extends Enum<E>>はタイプClass<?>の引数で呼び出されたときので、私たちは

<E extends Enum<E>> E getInstance(Class<E> c, String name) 

のパラメータを推測することはできません、互換性のある型の変数宣言ではないということです。 - そこにjavacができなかった直接mainメソッドでEnumWrapper.wrap(cast(e))を入れ

public class GenericEnum2 { 
    ... 
    public static void main(String[] args) { 

     ... 
     for(...) { 
      ... 
      try 
      { 
       Enum<?> e = getEnum(className); 
       EnumWrapper<?> wrapper = makeWrapper(e); 
       list.add(wrapper); 
      } 
      ... 
     } 
     ... 
    } 

    /** 
    * casts an instance of a Enum to its right type. 
    */ 
    static <E extends Enum<E>> E cast(Enum<E> e) { 
     // @SuppressWarning("unchecked") 
     E result = (E)e; 
     return result; 
    } 

    /** 
    * makes a wrapper for an enum instance. 
    * @see EnumWrapper#wrap 
    */ 
    static <E extends Enum<E>> EnumWrapper<E> makeWrapper(Enum<E> e) { 
     return EnumWrapper.wrap(cast(e)); 
    } 

    ... 

} 

は動作しませんでした:私は唯一の未チェックの警告が発生する方法を発見し、これは明確に明らかに虚偽の一つであり、言っ

ラップがキャストされたのと同じ型を受け入れると推論する。

いくつかのメソッドに分割して同様のキャストを行うことで、いくつかのSuppressWarningsをgetEnumメソッドで回避できますが、実際には試していません。

関連する問題