ジェネリック戻り値の上限 - インターフェースとクラス - 驚くほど有効なコード 質問する

ジェネリック戻り値の上限 - インターフェースとクラス - 驚くほど有効なコード 質問する

これはサードパーティのライブラリ API からの実際の例ですが、簡略化されています。

Oracle JDK 8u72でコンパイル

次の 2 つの方法を検討してください。

<X extends CharSequence> X getCharSequence() {
    return (X) "hello";
}

<X extends String> X getString() {
    return (X) "hello";
}

どちらも「未チェックのキャスト」警告を報告しています。理由はわかります。私を困惑させるのは、なぜ私が

Integer x = getCharSequence();

コンパイルできますか? コンパイラは がIntegerを実装していないことを認識する必要がありますCharSequence。 の呼び出し

Integer y = getString();

エラーが発生します(予想通り)

incompatible types: inference variable X has incompatible upper bounds java.lang.Integer,java.lang.String

この動作が有効であると考えられる理由を誰か説明できますか? どのように役立つのでしょうか?

クライアントはこの呼び出しが安全でないことを認識していません。クライアントのコードは警告なしでコンパイルされます。なぜコンパイル時に警告が出たりエラーが出たりしないのでしょうか?

また、この例とどう違うのでしょうか:

<X extends CharSequence> void doCharSequence(List<X> l) {
}

List<CharSequence> chsL = new ArrayList<>();
doCharSequence(chsL); // compiles

List<Integer> intL = new ArrayList<>();
doCharSequence(intL); // error

渡そうとするとList<Integer>、予想どおりエラーが発生します。

method doCharSequence in class generic.GenericTest cannot be applied to given types;
  required: java.util.List<X>
  found: java.util.List<java.lang.Integer>
  reason: inference variable X has incompatible bounds
    equality constraints: java.lang.Integer
    upper bounds: java.lang.CharSequence

それがエラーとして報告されるのなら、なぜInteger x = getCharSequence();報告されないのでしょうか?

ベストアンサー1

CharSequenceは です。したがって、が実装していなくてinterfaceも、クラスを作成することは完全に可能です。SomeClassCharSequence

class SubClass extends SomeClass implements CharSequence

したがって、次のように書くことができます

SomeClass c = getCharSequence();

推論された型Xが交差型であるためですSomeClass & CharSequence

これは の場合少し奇妙です。なぜならIntegerIntegerfinal ですが、finalこれらのルールでは何の役割も果たさないからです。たとえば、次のように書くことができます。

<T extends Integer & CharSequence>

一方、Stringは ではないため、 を拡張してのサブタイプを取得することinterfaceは不可能です。これは、Java がクラスの多重継承をサポートしていないためです。SomeClassString

このList例では、ジェネリックは共変でも反変でもないということを覚えておく必要があります。つまり、 がXのサブタイプである場合YList<X>は のサブタイプでもスーパータイプでもありません。は を実装していないList<Y>ため、メソッドでを使用することはできません。IntegerCharSequenceList<Integer>doCharSequence

ただし、これをコンパイルすることはできます

<T extends Integer & CharSequence> void foo(List<T> list) {
    doCharSequence(list);
}  

もしあなたが戻り値このようList<T>な:

static <T extends CharSequence> List<T> foo() 

できるよ

List<? extends Integer> list = foo();

これも、推論された型が でありInteger & CharSequence、これが のサブタイプであるためですInteger

複数の境界を指定すると、暗黙的に交差型が発生します (例<T extends SomeClass & CharSequence>)。

詳細については、ここJLSの一部で、型境界がどのように機能するかを説明します。複数のインターフェースを含めることができます。例:

<T extends String & CharSequence & List & Comparator>

ただし、最初の境界のみが非インターフェースになる場合があります。

おすすめ記事