2017-07-21 3 views
1

ジェネリックのラッパークラスの継承構造に問題があります。これは、基本的な構造です:汎用スーパークラスを返す

public abstract class SuperWrapper<E extends Super>{ 
    private E wrappedObject; 
    public E getWrappedObject(){ 
     return wrappedObject; 
    } 
} 

public abstract class MiddleWrapper<E extends Middle> extends SuperWrapper<E>{} 

public class SubAWrapper extends MiddleWrapper<SubA>{} 

public class SubBWrapper extends MiddleWrapper<SubB>{} 

包まれているクラスはとても基本的に、同じ継承構造に従います。

public class Super{} 

public class Middle extends Super{} 

public class SubA extends Middle{} 

public class SubB extends Middle{} 

これらのラップクラスは地雷ではないとの理由である(変更することはできませんラッパークラスの場合)。この構造体はほとんどの場合非常にうまく動作しますが、SubAWrapperまたはSubBWrapperのいずれかを返すメソッドが必要な場合にはいくつかの問題があります。

これは私がこれまで試したものです:

public static MiddleWrapper<?> findMiddle(String id){ 
    SubAWrapper a = new SubAWrapper(); 
    SubBWrapper b = new SubBWrapper(); 
    if(returnSubA){ 
     return a; 
    } else{ 
     return b; 
    } 
} 

これは、コンパイルして動作しますが、SonarQubeは、戻り値の型でのワイルドカードの使用について警告し、私のグーグルから、それはそのはずのいずれかの警告ではありません無視される。

別の方法:

public static <E extends Middle> MiddleWrapper<E> findMiddle(String id){ 
    SubAWrapper a = new SubAWrapper(); 
    SubBWrapper b = new SubBWrapper(); 
    if(returnSubA){ 
     return (MiddleWrapper<E>) a; 
    } else{ 
     return (MiddleWrapper<E>) b; 
    } 
} 

これは、コンパイルし、現在のコードで動作しますが、それは危険なようです。呼び出し元がMiddleWrapper<SubA> middle = findMiddle("1");のようにこのメソッドを使用すると、コンパイルは正常に行われますが、findMiddleがSubBWrapperを返そうとすると実行時にClassCastExceptionが発生します。何でも、私は仕事だろうが、していなかった:

public static MiddleWrapper<Middle> findMiddle(String id){ 
    SubAWrapper a = new SubAWrapper(); 
    SubBWrapper b = new SubBWrapper(); 
    if(returnSubA){ 
     return (MiddleWrapper<Middle>) a; //Compile error here 
    } else{ 
     return (MiddleWrapper<Middle>) b; //and here 
    } 
} 

だから私の質問は、基本的に、実行をコンパイルし、ベストプラクティスを、次の方法のfindMiddleを書き込むための正しい方法はありますか?

また、SubAとSubBの両方を含むリストを返すようにメソッドを書く方法がありますか?このようにワイルドカードなし:

public static List<MiddleWrapper<?>> getAllMiddle(){ 
    List<MiddleWrapper<?>> ret = Lists.newArrayList(); 
    ret.add(new SubAWrapper()); 
    ret.add(new SubBWrapper()); 
    return ret; 
} 

ps現時点ではまだJava 6を使用していますが、Java 8へのアップグレードは来年に予定されているため、Java 7-8の機能を使用するソリューションは引き続き有効です。

答えて

1

MiddleWrapperMiddleクラスでパラメータジェネリッククラスです:

public abstract class MiddleWrapper<E extends Middle> extends SuperWrapper<E>{} 

あなたがMiddleWrapperのために少なくとも2つの別個のジェネリック型でMiddleWrapperListを返す単一のメソッドを提供したい場合、あなたは何の多くを持っていません選択肢。

返されるかもしれないMiddleサブクラスに制約を設定する必要がない場合。あなたが示す
方法が細かいです:

public static List<MiddleWrapper<?>> getAllMiddle() { 
    List<MiddleWrapper<?>> ret = Lists.newArrayList(); 
    ret.add(new SubAWrapper()); 
    ret.add(new SubBWrapper()); 
    return ret; 
} 

は、あなたが言った:

これは、コンパイルして動作しますが、SonarQubeは、戻り値の型で、私はそれがありませんグーグルから ワイルドカードの使用について警告します無視する必要がある警告の1つです。

MiddleWrapper<?>?の使用はなぜ悪い習慣になるのですか?
クラスMiddleWrapperは一般的なものであり、あなたのメソッドでは、genericとして使用されている任意のタイプのMiddleを返すことがあります。
このユースケースのためのやり方です。

ここで、返されるかもしれないMiddleサブクラスに制約を設定する必要がある場合。
たとえば、一部のMiddleサブクラスではありません。

あなたは中東を拡張する仲介クラスを導入でき、SubASubBはそれを拡張し、getAllMiddle()方法でそれをListを入力します。

public class ValidMiddle extends Middle {...} 

public class SubA extends ValidMiddle{...} 

public class SubB extends ValidMiddle{...} 

及び方法は次のようになります。で指定された例では

List<MiddleWrapper<? extends ValidMiddle>> listOfWrappers = getAllMiddle(); 
+0

それは悪い習慣になる理由はここに少し説明しますhttps://sonarcloud.io/organizations/default/rules#q=wildcardをも様々にhttps://stackoverflow.com/questions/22815023/generic-wildcard-types-should-not-be-used-in-return-parametersなどのスタックオーバーフローに関する質問。 MiddleWrapper またはMiddleWrapper <?を使用できますか?中間>を返すタイプでは解決しやすいですが、問題がなければそれが可能かどうかです。 – MatsT

+0

これを実現するには、MiddleWrapperはジェネリックを指定せずにインスタンス化できる必要があります。ジェネリックのレイヤーを削除することで可能です。最後に、ジェネリック制約とAPIの使いやすさの間にトレードオフがあります。 – davidxxx

1

:この方法は、もう少し詳細なこの宣言された型を操作する必要があり、クライアントのためであることを

public static List<MiddleWrapper<? extends ValidMiddle>> getAllMiddleWithMoreSpecificType() { 
    List<MiddleWrapper<? extends ValidMiddle>> ret = Lists.newArrayList(); 
    ret.add(new SubAWrapper()); 
    ret.add(new SubBWrapper()); 
    return ret; 
} 

注意"別の方法"段落ClassCastExceptionは、返される型を強制することで回避できます。

public static <E extends Middle> MiddleWrapper<E> findMiddle(String id, Class<E> clazz){ 
     SubAWrapper a = new SubAWrapper(); 
     SubBWrapper b = new SubBWrapper(); 
     if(returnSubA){ 
     //check here if a/b is instance of clazz 
      return (MiddleWrapper<E>) a; 
     } else{ 
      return (MiddleWrapper<E>) b; 
     } 
    } 

そしてメソッド呼び出しは次のようになります。

MiddleWrapper<SubA> middle = findMiddle("1", SubA.class); 
関連する問題