2017-08-10 10 views
1

私はこのインタフェースを持っている

public class Preparer { 
    public <T extends Inflatable> void inflate(T thingToInflate) { 

     int pressure = thingToInflate.getPump().readPressure(thingToInflate); 
    } 
} 

このエラーと、コンパイルされません。

The method readPressure(capture#1-of ? extends Inflatable) in the type Pump is not applicable for the arguments (T)

ここで何が間違っていますか?変数thingToInflateはサブクラスInflatable<T extends Inflatable>、右?)のインスタンスでなければならず、readPressureメソッドはInflatableのサブクラスを必要とするように定義されています。

私は、この特定の例では、不自然であることを知っているが、一般的なケースは、Tのインスタンス与えられ、Iは、次にまったく同じ方法でTを定義するために表示される別のクラスのメソッドにそのインスタンスを渡すことができないことです。これを修正できますか?

+0

あなたが予期している用途が 'int readPressure(Inflatable)'を示唆しているので、 'Pump'は実際には一般的ではないはずです。おそらく[PECS](https://stackoverflow.com/q/2723397/2891664)も参照してください。 – Radiodef

答えて

1

getPumpが返したPumpは、Pump<T>ではない可能性があります。 を返します。Uは、Inflatableに拡張されたものです。 TUのサブタイプであると仮定することは安全ではありません。

InflatableC1C2を実装する2つの具体的なクラスがあるとしましょう。 getPumpは、Pump<C1>のインスタンスを返すことがあります。 TC2であるとします。タイプC2のオブジェクトはC1のインスタンスではないため、readPressureメソッドに渡すことはできません。

そのため、タイプセーフティ違反なしで「修正」できないのはこのためです。ここで

は、あなたが間違ったことをやろうとしていることを示す具体的な例です:あなたが行うことができます

class C1 implements Inflatable, Pump<C1> { 
     @Override 
     public Pump<? extends Inflatable> getPump() { 
      return this; // an instance of C1 which implements Pump<C1> 
     } 

     @Override 
     public int readPressure(C1 thingToInflate) { 
      return 0; 
     } 
    } 

    class C2 implements Inflatable { 
     @Override 
     public Pump<? extends Inflatable> getPump() { 
      return new C1(); // again, an instance of C1 which implements Pump<C1> 
     } 
    } 

    public class Preparer { 
     public <T extends Inflatable> void inflate(T thingToInflate) { 
      int pressure = thingToInflate.getPump().readPressure(thingToInflate); 
      // Let's assume that it were possible. What happens if one calls 
      // new Preparer().inflate(new C2())? 
      // new C2().getPump() returns an instance of C1 which implements Pump<C1> 
      // It's readPressure method expects an instance of C1. But T = C2, so 
      // the thingToInflate is not an instance of C1. 
      // If the compiler allowed this to happen, the type safety 
      // would be violated. 
     } 
    } 

唯一のことは、あなたのインターフェイスを再設計されています。私はあなたのコードが最初に達成しようとしているものがわからないので、修正する正確な方法を教えてくれません。

1

戻り値の型がTのメソッドを書くとき、返されるものがTのサブクラスになることはできません。コンパイラに関する限り、型がTであることを意味します。メソッドがListを返す場合と同様に、返されるのはLinkedListまたはArrayListなどです。

Tを一般的な型に拡張すると指定すると、コンパイル時の型はTの範囲である可能性がありますが、Tを拡張する任意の型になる可能性があります。型はinstanceofのようなものを使用せず、その後、ジェネリックを使う目的を破ってキャスティングします。

タイプの範囲を指定すると、メソッドのパラメータに意味があります。私がT extends Animalのリストを取るメソッドを持っていれば、呼び出し元はDogのリストまたはCatのListを渡すことができます。他の文脈では、それは役に立ちません。

戻り値の型にextends/superを使用してワイルドカードを使用しないでください。引用するこれを言っ効果的なJavaでのエントリ(第5章、項目28)が、あります:

Do not use wildcard types as return types. Rather than providing additional flexibility for your users, it would force them to use wildcard types in client code.

(それは本の中で表示されるテキストが太字にされ、それは私が紹介したものではありません。)(注)この引用こと有界ワイルドカードの使用の議論の中にあります。正真正銘の方法を使用して、クライアントが(メソッドから

Class<?> 

を返すインスタンスの)タイプが何であるかを気にしない場合は、先に行きます。