7

Source<T>オブジェクトの純粋なプロデューサーである汎用インターフェースがあるとしTます。純粋なプロデューサーであることは、インターフェイスの契約の一部です。したがって、 を使用してできることは、 を持っている場合にも実行できるはずであるというのは妥当な期待です。Source<Foo>Source<? extends Foo>

ここで、この制限を の本体に適用する必要があります。これにより、誰かがその契約に反する方法でSource誤って使用しないようにすることができます。T

JDK の例

@Miserable.Variable が指摘するように、同等ではArrayList<Integer>ありません。これは、ジェネリック型として共変ではないためです。言い換えれば、は;の純粋なプロデューサーではありません。具体的には、このメソッドは を消費します。ArrayList<? extends Integer>ArrayListArrayList<T>TArrayListadd(T) T

Iteratorただし、またはのような純粋なプロデューサーであるジェネリック型がありますIterable。でできることは何でも、 で行うIterator<Integer>こともできますIterator<? extends Integer>。のような方法はありませArrayList.add(T)Iterator<T>

Source<T>私のインターフェースが好きIterator<T>ではなく似ていることを確認したいだけですArrayList<T>。将来、誰かが をT消費するメソッド ( などadd(T)) を私のインターフェースに追加した場合、明示的なエラーが発生するようにしたいと考えています。

より複雑な例

タイプのパラメーターがTインターフェイスに表示されないようにするだけでは、完全な解決策にはなりません。Tまた、他のジェネリック型への引数として使用される可能性があることに注意する必要があります。たとえば、次のメソッドは では許可されませんSource<T>

public void copyTo(List<T> destination);

卑劣なサブクラスがリストから読み取ろうとする可能性があるため、それはT-consumer と見なされます。でこのメソッドを呼び出すことはできませんSource<? extends Foo>。一方、これは許可されるべきです:

public void copyTo(List<? super T> destination);

Source<T>( のメソッドはを返すことはできませんがList<T>、 を返すことはできるという別の規則もありList<? extends T>ます。)

現在、実際のインターフェースは、多くのメソッドを使用して任意に複雑にすることができ、ルール自体もかなり複雑です。間違いを犯すのは非常に簡単です。だから私はこのチェックを自動化したい。

単体テストのトリック、静的アナライザー、コンパイラ/IDE プラグイン、注釈プロセッサ (たとえば、@Covariant注釈付きT)、またはこれを保証できるその他の手法やツールはありますか?

4

3 に答える 3

2

これは答えではありませんが、長すぎてコメントに収まりません。

したがって、 でできることは何でもSource<Foo>Source<? extends Foo>

いいえ、それは合理的な期待ではありません。PDF全体にリンクし、トップレベルのページFoo<T>に移動するため、これが合理的であるとどのように判断したかは不明ですが、一般に aを a に任意に置き換えることはできませんFoo<? extends T>. たとえば、 があるArrayList<Integer> a場合は呼び出すことができますが、 がある場合は a.Add(Interger.valueOf(5))それを行うことはできません。aArrayList<? extends Integer> a

Consumer<T>とが何であるかも不明sendToです。後者はSource<T>>のメソッドですか?

これらの説明がなければ、彼の質問があいまいではないかと思います。

于 2012-12-28T19:30:05.200 に答える
2

インターフェイスに put() メソッドを一切入れたくない場合は、何も記述せず、コードのどこかにその旨のコメントを残すだけで済みます。できれば多くのアスタリスクで囲みます。

正直なところ、クラスやインターフェースに対するいくつかの反変メソッドをブロックする可能性を言語に与える機能を持つことの有用性がまったく理解できません。これは、コンパイラーではなく、プログラマーの責任です。

これは、Integer をパラメーターの 1 つまたは戻り値として受け入れる関数の追加をブロックする可能性を提供する機能を求めるのと同じことです。私の意見では、言語にそのような機能を持たせることはまったく役に立たないでしょう。

于 2012-12-29T15:47:49.763 に答える
0

探している答えではないかもしれませんが、インターフェース宣言でそれを強制できると思います。

反変の使用を強制する

public interface Consumer<T, S extends T> {
   public void consume(S item);
}

public static class ConsumerImpl<T, S extends T> implements Consumer<T, S> {

   private List<? super S> items = new ArrayList<T>();

   public void consume(S item) {
      this.items.add(item);
   }

}

次に、次のように使用できます。

Consumer<Object, String> consumer = new ConsumerImpl<Object, String>();
consumer.consume("Whatever");
consumer.consume("Another");

共変使用を強制する

もちろん、逆も可能です。

public interface Producer<T, S extends T> {
   public T produce();
}

public static class ProducerImpl<T, S extends T> implements Producer<T,S> {

   private Deque<? extends T> items = new ArrayDeque<S>();

   public ProducerImpl(Deque<S> items) {
      this.items = items;
   }

   public T produce() {
      return items.pop();
   }

}

そして、次のように使用できます。

Deque<Integer> myInts = new ArrayDeque<Integer>();
myInts.push(1);
myInts.push(2);

Producer<Number, Integer> producer = new ProducerImpl<Number, Integer>(myInts);
Number n1 = producer.produce();
Number n2 = producer.produce();

どちらの場合も、基礎となる構造が反変または共変であることを強制しました。

于 2012-12-29T16:47:01.300 に答える