W jaki sposób JLS określa, że symboli wieloznacznych nie można formalnie używać w metodach?
Zawsze zastanawiałem się nad jakimś dziwnym aspektem generics Java i używaniem symboli wieloznacznych. Powiedzmy, że mam następujący interfejs API:
public interface X<E> {
E get();
E set(E e);
}
A następnie, powiedzmy, deklarujemy następujące metody:
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());
}
}
Z mojego „intuicyjnego” zrozumieniaX<?>
jest w rzeczywistości taki sam jakX<T>
, z wyjątkiem tego, co nieznane<T>
jestformalnie nieznany kodowi klienta. Ale w środkufoo()
, Zgaduję, że kompilator może wywnioskować (pseudo kod JLS)<T0> := <?>[0], <T1> := <?>[1], etc...
. To właśnie robi większość programistów w sposób jawny i intuicyjny. Przekazują prywatną metodę pomocniczą, co prowadzi do wielu niepotrzebnych kodów płyt kotłowych:
public class Foo {
public void foo(X<?> x) {
foo0(x);
}
private <T> void foo0(X<T> x) {
x.set(x.get());
}
}
Inny przykład:
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());
}
}
Innymi słowy, kompilator wie, że znak wieloznaczny jest wx.set()
jest formalnie taki sam jak znak wieloznacznyx.get()
. Dlaczego nie może wykorzystać tych informacji? Czy istnieje aspekt formalny wJLS, to wyjaśnia tę brakującą kompilator „funkcję”?