私はいつも、Javaジェネリックの奇妙な側面とワイルドカードの使用について疑問に思っていました。たとえば、次のAPIがあります。
public interface X<E> {
E get();
E set(E e);
}
次に、次のメソッドを宣言するとします。
public class Foo {
public void foo(X<?> x) {
// This does not compile:
x.set(x.get());
}
public <T> void bar(X<T> x) {
// This compiles:
x.set(x.get());
}
}
私の「直感的な」理解から、未知のものがクライアントコードに正式に未知であるということを除いて、X<?>
実際にはと同じです。しかし、の内部では、コンパイラーが(疑似JLSコード)を推測できると思います。これは、ほとんどのプログラマーが明示的かつ直感的に行うことです。彼らはプライベートヘルパーメソッドに委任します。これは多くの役に立たないボイラープレートコードにつながります。X<T>
<T>
foo()
<T0> := <?>[0], <T1> := <?>[1], etc...
public class Foo {
public void foo(X<?> x) {
foo0(x);
}
private <T> void foo0(X<T> x) {
x.set(x.get());
}
}
もう一つの例:
public class Foo {
public void foo() {
// Assuming I could instanciate X
X<?> x = new X<Object>();
// Here, I cannot simply add a generic type to foo():
// I have no choice but to introduce a useless foo0() helper method
x.set(x.get());
}
}
つまり、コンパイラは、のワイルドカードx.set()
が形式的にはのワイルドカードと同じであることを認識していx.get()
ます。なぜその情報を使用できないのですか?JLSには、この不足しているコンパイラの「機能」を説明する形式的な側面がありますか?