12

同じタイプ(特定のタイプのセットのいずれかである可能性があるタイプ)の2つの値のペアを表すクラスがあります。

  public class Pair<E extends AClass>{
      private E var1;
      private E var2; 
  }

このクラスはフレームワークによって使用されるため、2つの変数(var1、var2)をインスタンス化する必要がある引数のないコンストラクターが必要です。

  public class Pair<E extends AClass>{
      private E var1;
      private E var2; 

      public Pair(){
           var1 = invoke constructor of type E; 
           var2 = invoke constructor of type E 
      }
  }

ここには明らかに多くの問題があります。

  1. 変数をインスタンス化するには、どういうわけかその正確な型を知り、その特定の型のコンストラクターを呼び出す必要があります。最良の場合、これはコンストラクターにかなり大きなifelseステートメントがあることを意味します。

     public Pair(){
           if(var1 instanceof SpecificType1){
              var1 = new SpecificType1(); 
              var2 = new SpecificType2();
           }
      }
    
  2. 上記のように行っても、var1がタイプEで宣言されているため、いくつかの問題が発生します。SpecficType1をインスタンス化して、結果のオブジェクトをvar1 / var2に割り当てようとすると、タイプの不一致エラーが発生します。それを機能させるために、私はEにキャストする必要があります:

       var1 = (E)new SpecificType1();
    

しかし、特定の型をジェネリック型にキャストしようとしているので、これはコンパイル時の型チェックを破壊します。

これはJavaのジェネリックスの制限ですか、それともこのシナリオはジェネリックスを使用するのに悪いシナリオですか?

4

3 に答える 3

7

変数をインスタンス化するには、どういうわけかその正確な型を知り、その特定の型のコンストラクターを呼び出す必要があります。最良の場合、これはコンストラクターにかなり大きなifelseステートメントがあることを意味します。

その前に問題が発生します。

   if(var1 instanceof SpecificType1){
      var1 = new SpecificType1(); 
      var2 = new SpecificType2();
   }

var1nullこの時点で、すべてvar1 instanceof Tの。falseT


Javaジェネリックスの制限の1つは、ジェネリック型パラメーターが消去されるため、引数がゼロのコンストラクターから型パラメーターを反映する方法がないことです。

呼び出し元は、初期化との方法を指示するためのコンテキストを提供する必要がvar1ありvar2、そのコンテキストを提供する一般的な方法は、コンストラクター引数を使用することです。


おそらく、最初から始めて、必要なコンテキストを取得できるようになるまで初期化を遅らせるのが最善のvar1オプションvar2ですnull

多分

void init(Class<E> type) {
  if (type.isAssignableFrom(ConcreteType1.class)) {
    var1 = type.cast(new ConcreteType1(...));
    var2 = type.cast(new ConcreteType1(...));
  } else { /* other branches */ }
}

E extends List<String>まだ区別できないため、これは完全ではありませんE extends List<Number>が、ケースには十分である可能性があり、この.castメソッドは、にタイプセーフなキャストを提供しEます。


あるいは、Guava、Guice、および関連するライブラリSupplier<E>は、メソッドで役立つインターフェイスなどを提供しますinit

于 2012-09-22T23:09:35.803 に答える
2

ジェネリック型をインスタンス化することはできません-たとえば、ジェネリック型が次の場合はどうなりSomeAbstractClassますか?何がインスタンス化されますか?(これは理由ではなく、単なる直感です)

ただし、JavaリフレクションAPIを使用してオブジェクトをインスタンス化することはできますが、そのための特定のクラスオブジェクトが必要になります。

より洗練された代替手段は、抽象ファクトリデザインパターンを使用し、ファクトリオブジェクトをペアに渡し、それを使用して必要なオブジェクトを構築することです。


コードサンプル:

public class Pair<S> {
    public final S var1; 
    public final S var2;
    public Pair(Factory<S> builder) {
        var1 = builder.build();
        var2 = builder.build();

    }
}

public interface Factory<S> { 
    public S build();
}

public class IntegerBuilder implements Factory<Integer> {
    private int element = 5;
    public Integer build() {
        return new Integer(element++);
    }
}
于 2012-09-22T23:11:35.863 に答える
1

フレームワークがそれをインスタンス化する場合、それは生の型としてそれを行いnew Pair()ます。これは、型パラメーターがない場合と同等です。

次のような単純なワンライナークラスを作成する必要があると思います。

class SpecificType1Pair extends Pair<SpecificType1> {}

代わりにそれらをフレームワークに渡します。実際のタイプパラメータはとして取得できますgetClass().getGenericSuperclass()).getActualTypeArguments()[0]。クラスペアは次のようになります。

public abstract class Pair<E extends AClass> {
    private E var1;
    private E var2;

    public Pair() {
        ParameterizedType superclass = (ParameterizedType) getClass().getGenericSuperclass();
        @SuppressWarnings("unchecked")
        Class<E> clazz = (Class<E>) superclass.getActualTypeArguments()[0];
        try {
            var1 = clazz.newInstance();
            var2 = clazz.newInstance();
        } catch (InstantiationException e) {
            handle(e);
        } catch (IllegalAccessException e) {
            handle(e);
        }
    }
}
于 2012-09-23T00:28:55.827 に答える