質問は、オブジェクト型の変数をn
型の変数に追加することが機能するかどうかを尋ねました(つまり. )Nissan
c
List<Car>
insertCar(c, n)
あなたの言葉をコード化すると、これがあなたが話していることだと思います:
public class GenericCheck {
static void insertCar(List<? super Car> c, Car x) {
c.add(x);
}
public static void main(String[] args) {
List<Car> c = new ArrayList<>();
Nissan n = new Nissan();
insertCar(c, n);
}
}
class Car {}
class Nissan extends Car {}
はい。これは問題ありませんが、実行する必要がある 3 つの型チェックがあります。
- の 1 番目の引数は
List<Car>
有効ですか? はい、サブタイプです。たとえば、こちらを参照してください。insertCar
List<? super Car>
- の 2 番目の引数は
Nissan
有効ですか? はい、サブタイプです。insertCar
Car
- 呼び出しは
c.add(x)
有効ですか? はい、引数は で定義されているタイプまたはスーパータイプでx
なければなりません。これは定義によるものです。Car
Car
List<? super Car> c
Car x
では、なぜ混乱するのでしょうか。Nissan
2 番目の引数に型を渡すため、ポイント 3 が型またはスーパータイプではない(サブタイプである)ため、ポイント 3 が壊れると考えているためです。何が起こるかというと、それが にアップキャストされ、これが実行可能な引数になります。x
Car
Car
Nissan
Car
呼び出しが有効かどうかを知るには、 (メソッドの引数リストで) andc.add(x)
の定義を確認するだけでよいことに注意してください。これにより、メソッドが有効な引数で呼び出される限り、その呼び出しは有効になります。このチェックは、 を呼び出すときの (2 つの) タイプ チェックとは関係ありません。これらは、上記で実行した 3 つのチェックです。c
x
c.add(x)
insertCar
編集: では、なぜうまくいかinsertCar(List<? super Nissan> c, Car x)
ないのですか?
型消去のため。コンパイル中に、コンパイラはすべての型パラメーターを消去し、それぞれを最初の境界に置き換えます (こちらを参照)。あなたが得るのは へのリクエストList<Nissan>
ですadd(Car)
。しかし、これはCar
のサブタイプではないため、コンパイルできませんNissan
。
の最初のケースではList<? super Car>
、型消去は結果として有効にList<Car>
なりadd(Car)
ます。
私が思うに、あなたの混乱を最も解消するのは、ジェネリックがコンパイル時のみのチェックを提供するという認識です。上記のコードブロックでほのめかしたように
static void insertCar(List<? super Car /* or Nissan */> c, Car x) {
c.add(x);
}
実行時にメソッドが呼び出される引数に関係なく、コンパイルする必要があります。これは、1 番目のメソッド引数のジェネリックが、2 番目の引数に渡す型とはまったく関係がないこと、insertCar(..., Car)
またはinsertCar(..., Nissan)
のコンパイル可能性に影響を与えないことを意味しc.add(x)
ます。指定された引数はメソッドの引数の型に変換 (アップキャスト) されますがCar
、これはメソッドの内容とは無関係です。