Как JLS определяет, что подстановочные знаки не могут быть формально использованы внутри методов?
Я всегда интересовался каким-то странным аспектом обобщений 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());
}
}
Из моего & quot; интуитивного & quot; понимание,X<?>
на самом деле так же, какX<T>
разве что неизвестно<T>
являетсяformally неизвестный клиенту код. Но внутриfoo()
Я бы предположил, что компилятор может сделать вывод (псевдо-JLS-код)<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, что объясняет отсутствие функции компилятора?