Como o JLS especifica que os curingas não podem ser formalmente usados nos métodos?
Eu sempre estive pensando sobre algum aspecto estranho dos genéricos Java e o uso de curingas. Digamos que eu tenha a seguinte API:
public interface X<E> {
E get();
E set(E e);
}
E então, digamos que declaramos os seguintes métodos:
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());
}
}
Do meu entendimento "intuitivo",X<?>
é de fato o mesmo queX<T>
, exceto que o desconhecido<T>
éformalmente desconhecido para o código do cliente. Mas dentro defoo()
, Eu diria que o compilador poderia inferir (pseudo código JLS)<T0> := <?>[0], <T1> := <?>[1], etc...
. Isso é o que a maioria dos programadores faz de forma explícita e intuitiva. Eles delegam a um método auxiliar privado, o que leva a muitos códigos inúteis de placa de caldeira:
public class Foo {
public void foo(X<?> x) {
foo0(x);
}
private <T> void foo0(X<T> x) {
x.set(x.get());
}
}
Outro exemplo:
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());
}
}
Em outras palavras, o compilador sabe que o curinga emx.set()
é formalmente o mesmo que o curinga emx.get()
. Por que não pode usar essa informação? Existe um aspecto formal noJLS, isso explica esse "recurso" do compilador que está faltando?