これはサードパーティのライブラリ 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
も、クラスを作成することは完全に可能です。SomeClass
CharSequence
class SubClass extends SomeClass implements CharSequence
したがって、次のように書くことができます
SomeClass c = getCharSequence();
推論された型X
が交差型であるためですSomeClass & CharSequence
。
これは の場合少し奇妙です。なぜならInteger
はInteger
final ですが、final
これらのルールでは何の役割も果たさないからです。たとえば、次のように書くことができます。
<T extends Integer & CharSequence>
一方、String
は ではないため、 を拡張してのサブタイプを取得することinterface
は不可能です。これは、Java がクラスの多重継承をサポートしていないためです。SomeClass
String
このList
例では、ジェネリックは共変でも反変でもないということを覚えておく必要があります。つまり、 がX
のサブタイプである場合Y
、List<X>
は のサブタイプでもスーパータイプでもありません。は を実装していないList<Y>
ため、メソッドでを使用することはできません。Integer
CharSequence
List<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>
ただし、最初の境界のみが非インターフェースになる場合があります。