web-dev-qa-db-ja.com

ジェネリック<の実際の例は何ですか?スーパーT>?

<? super T>は、Tのスーパークラス(任意のレベルのTの親クラス)を表すことを理解しています。しかし、私はこの一般的なバインドされたワイルドカードの実際の例を想像するのに本当に苦労しています。

<? super T>の意味を理解し、このメソッドを見ました:

public class Collections {
  public static <T> void copy(List<? super T> dest, List<? extends T> src) {
      for (int i = 0; i < src.size(); i++)
        dest.set(i, src.get(i));
  }
}

私は実生活のユースケースの例を探していますが、この構造を使用することができますが、それが何であるかの説明ではありません。

71
BanzaiTokyo

私が考えることができる最も簡単な例は次のとおりです。

public static <T extends Comparable<? super T>> void sort(List<T> list) {
    list.sort(null);
}

同じCollectionsから取得されます。このようにDogComparable<Animal>を実装でき、Animalがすでに実装している場合、Dogは何もする必要がありません。

実際の例では編集:

Ping-pongsをメールで送信した後、職場から実際の例を提示することができます(はい!)。

Sinkと呼ばれるインターフェイスがあります(何をするかは関係ありません)、アイデアはaccumulates事です。宣言は非常に簡単です(単純化されています):

interface Sink<T> {
    void accumulate(T t);
}

明らかにListを取り、その要素をSinkにドレーンするヘルパーメソッドがあります(少し複雑ですが、簡単にするために):

public static <T> void drainToSink(List<T> collection, Sink<T> sink) {
    collection.forEach(sink::accumulate);
}

これは簡単ですよね?まあ...

List<String>を持つことはできますが、それをSink<Object>に排出したいです-これは私たちにとってかなり一般的なことです。しかし、これは失敗します:

Sink<Object> sink = null;
List<String> strings = List.of("abc");
drainToSink(strings, sink);

これが機能するには、宣言を次のように変更する必要があります。

public static <T> void drainToSink(List<T> collection, Sink<? super T> sink) {
    ....
}
46
Eugene

このクラス階層があるとします:CatはMammalから継承し、MammalはAnimalから継承します。

List<Animal> animals = new ArrayList<>();
List<Mammal> mammals = new ArrayList<>();
List<Cat> cats = ...

これらの呼び出しは有効です。

Collections.copy(animals, mammals); // all mammals are animals
Collections.copy(mammals, cats);    // all cats are mammals
Collections.copy(animals, cats);    // all cats are animals
Collections.copy(cats, cats);       // all cats are cats 

しかし、これらの呼び出しはnot有効です:

Collections.copy(mammals, animals); // not all animals are mammals
Collections.copy(cats, mammals);    // not all mammals are cats
Collections.copy(cats, animals);    // mot all animals are cats

したがって、メソッドシグネチャは、より具体的な(継承階層の下位)クラスからより一般的なクラス(継承階層の上位)にコピーすることを保証するだけであり、逆ではありません。

14
Benoit

たとえば、 Collections.addAll メソッドの実装を調べます。

public static <T> boolean addAll(Collection<? super T> c, T... elements) {
    boolean result = false;
    for (T element : elements)
        result |= c.add(element);
    return result;
}

ここで、エレメントは、エレメントタイプがエレメントのTタイプのスーパータイプである任意のコレクションに挿入できます。

下限のワイルドカードなし:

public static <T> boolean addAll(Collection<T> c, T... elements) { ... }

次は無効でした:

List<Number> nums = new ArrayList<>();
Collections.<Integer>addAll(nums , 1, 2, 3);

というのは、Collection<T>という用語はCollection<? super T>よりも限定的だからです。


別の例:

Predicate<T> Javaのインターフェイス。次のメソッドで<? super T>ワイルドカードを使用します。

default Predicate<T> and(Predicate<? super T> other);

default Predicate<T>  or(Predicate<? super T> other);

<? super T>を使用すると、次のように、さまざまな述部をより広範囲にチェーンできます。

Predicate<String> p1 = s -> s.equals("P");
Predicate<Object> p2 = o -> o.equals("P");

p1.and(p2).test("P"); // which wouldn't be possible with a Predicate<T> as a parameter
5

メソッドがあるとします:

passToConsumer(Consumer<? super SubType> consumer)

次に、Consumerを使用できるSubTypeを使用してこのメ​​ソッドを呼び出します。

passToConsumer(Consumer<SuperType> superTypeConsumer)
passToConsumer(Consumer<SubType> subTypeConsumer)
passToConsumer(Consumer<Object> rootConsumer)

例:

class Animal{}

class Dog extends Animal{

    void putInto(List<? super Dog> list) {
        list.add(this);
    }
}

したがって、DogList<Animal>またはList<Dog>に入れることができます。

List<Animal> animals = new ArrayList<>();
List<Dog> dogs = new ArrayList<>();

Dog dog = new Dog();
dog.putInto(dogs);  // OK
dog.putInto(animals);   // OK

putInto(List<? super Dog> list)メソッドをputInto(List<Animal> list)に変更した場合:

Dog dog = new Dog();

List<Dog> dogs = new ArrayList<>();
dog.putInto(dogs);  // compile error, List<Dog> is not sub type of List<Animal>

またはputInto(List<Dog> list)

Dog dog = new Dog();

List<Animal> animals = new ArrayList<>();
dog.putInto(animals); // compile error, List<Animal> is not sub type of List<Dog>
3
xingbin

この簡単な例を考えてみましょう。

List<Number> nums = Arrays.asList(3, 1.2, 4L);
Comparator<Object> numbersByDouble = Comparator.comparing(Object::toString);
nums.sort(numbersByDouble);

うまくいけば、これはやや説得力のあるケースです:表示目的のために数字をソートしたいことを想像できます(toStringは合理的な順序です)が、Number自体は比較できません。

integers::sortComparator<? super E>を取るため、これはコンパイルされます。 Comparator<E>(この場合ENumberである)だけを使用した場合、Comparator<Object>Comparator<Number>のサブタイプではないため、コードはコンパイルに失敗します(質問が既に理解していることを示しているため、私は入りません) )。

1
yshavit

ウェブラジオを書いたので、クラスMetaInformationObjectがありました。これはPLSおよびM3Uプレイリストのスーパークラスでした。選択ダイアログがあったので、

public class SelectMultipleStreamDialog <T extends MetaInformationObject>
public class M3UInfo extends MetaInformationObject
public class PLSInfo extends MetaInformationObject

このクラスにはメソッドpublic T getSelectedStream()がありました。
したがって、呼び出し元は、具体的なタイプ(PLSまたはM3U)のTを受け取りましたが、スーパークラスで作業する必要があるため、リストがありました:List<T super MetaInformationObject>。結果が追加された場所。
これは、一般的なダイアログが具体的な実装をどのように処理し、残りのコードがスーパークラスで機能するかということです。
もう少し明確になることを願っています。

1
ItFreak

ここでは、コレクションが良い例となります。

1 で述べたように、List<? super T>を使用すると、Listよりも派生しないタイプの要素を保持するTを作成できるため、Tから継承し、Tのタイプで、Tが継承する要素を保持できますから。

一方、List<? extends T>を使用すると、Listから継承する要素のみを保持できるTを定義できます(場合によっては、T型でもない)。

これは良い例です。

public class Collections {
  public static <T> void copy(List<? super T> dest, List<? extends T> src) {
      for (int i = 0; i < src.size(); i++)
        dest.set(i, src.get(i));
  }
}

ここでは、派生の少ないタイプのListを、派生の少ないタイプのListに投影します。ここでList<? super T>は、srcのすべての要素が新しいコレクションで有効であることを保証します。

1Javaの<?super T>と<?extends T>の違い

1
Michał Turczyn

いくつかの実際の例がこのために思い浮かびます。最初に取り上げたいのは、「即興」機能に使用される現実世界のオブジェクトのアイデアです。ソケットレンチがあるとします。

public class SocketWrench <T extends Wrench>

ソケットレンチの明白な目的は、Wrenchとして使用されることです。ただし、ピンチでレンチを使用して釘を打つことができると考える場合、次のような継承階層を作成できます。

public class SocketWrench <T extends Wrench>
public class Wrench extends Hammer

このシナリオでは、SocketWrenchの特殊な使用と見なされる場合でも、socketWrench.pound(Nail nail = new FinishingNail())を呼び出すことができます。

ずっと、SocketWrenchは、SocketWrenchではなくWrenchの代わりにHammerとして使用されている場合、applyTorque(100).withRotation("clockwise").withSocketSize(14)などのメソッドを呼び出すことができるようになります。

0
John Stark

あなたが持っていると言う:

class T {}
class Decoder<T>
class Encoder<T>

byte[] encode(T object, Encoder<? super T> encoder);    // encode objects of type T
T decode(byte[] stream, Decoder<? extends T> decoder);  // decode a byte stream into a type T

その後:

class U extends T {}
Decoder<U> decoderOfU;
decode(stream, decoderOfU);     // you need something that can decode into T, I give you a decoder of U, you'll get U instances back

Encoder<Object> encoderOfObject;
encode(stream, encoderOfObject);// you need something that can encode T, I give you something that can encode all the way to Java.lang.Object
0
memo