Suppose we have a class like this:
public class xx {
public interface Foo<T> {
T getValue();
void setValue(T value);
}
public void resetFoos(Iterable<Foo<?>> foos) {
for (Foo<?> foo : foos)
foo.setValue(foo.getValue());
}
}
It will fail to compile even though intuitively it seems like it "should":
xx.java:10: setValue(capture#496 of ?) in xx.Foo<capture#496 of ?> cannot be applied to (java.lang.Object)
foo.setValue(foo.getValue());
The reason is that foo
does not have a bound generic type, so the compiler doesn't "know" that the output of foo.getValue()
is compatible with the input of foo.setValue()
.
So to fix this you have to create a new method just for the purpose of binding the generic type parameter in the for()
loop:
public class xx {
public interface Foo<T> {
T getValue();
void setValue(T value);
}
public void resetFoos(Iterable<Foo<?>> foos) {
for (Foo<?> foo : foos)
this.resetFoo(foo);
}
// stupid extra method here just to bind <T>
private <T> void resetFoo(Foo<T> foo) {
foo.setValue(foo.getValue());
}
}
This has always annoyed me. Plus, it seems like there can be a simple solution.
My question: Is there any "good" reason why the java language shouldn't be extended to allow generic type declarations on variable declarations? For example:
public class xx {
public interface Foo<T> {
T getValue();
void setValue(T value);
}
public void resetFoos(Iterable<Foo<?>> foos) {
for (Foo<?> foo : foos) {
final <T> Foo<T> typedFoo = foo;
foo.setValue(foo.getValue());
}
}
}
or, more concisely in this case of a for()
loop:
public class xx {
public interface Foo<T> {
T getValue();
void setValue(T value);
}
public void resetFoos(Iterable<Foo<?>> foos) {
for (<T> Foo<?> foo : foos)
foo.setValue(foo.getValue());
}
}
I'm wondering if some compiler wizard can explain why this would either be too hard, or can (and should) be done.
EDIT:
In response to this suggested solution:
public <T> void resetFoos(Iterable<Foo<T>> foos) {
for (Foo<T> foo : foos) {
foo.setValue(foo.getValue());
}
}
This method signature doesn't allow Foo
s with various generic types to be reset together. In other words, trying to pass in an Iterable<Foo<?>>
causes a compile error.
This example demonstrates the problem:
public static class FooImpl<T> implements Foo<T> {
private T value;
public FooImpl(T value) { this.value = value; }
@Override public T getValue() { return value; }
@Override public void setValue(T value) { this.value = value; }
}
public static <T> void resetFoos(Iterable<Foo<T>> foos) {
for (Foo<T> foo : foos) {
foo.setValue(foo.getValue());
}
}
public static void main(String[] args) {
final Foo<Object> objFoo = new FooImpl<>(new Object());
final Foo<Integer> numFoo = new FooImpl<>(new Integer(42));
final Foo<String> strFoo = new FooImpl<>("asdf");
List<Foo<?>> foos = new ArrayList<>(3);
foos.add(objFoo);
foos.add(numFoo);
foos.add(strFoo);
resetFoos(foos); // compile error
System.out.println("done");
}
The compile error reads:
method resetFoos
cannot be applied to given types;
required: Iterable<Foo<T>>
found: List<Foo<?>>
reason: no instance(s) of type variable(s) T
exist so that argument type List<Foo<?>>
conforms to formal parameter type Iterable<Foo<T>>
where T
is a type-variable:
T extends Object
declared in method <T>resetFoos(Iterable<Foo<T>>)
(using sun-jdk-1.7.0_10 via ideone.com)