Any reason why Java should not allow generic type declarations on a variable declaration?

advertisements

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 Foos 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)


Basically, type variables can only currently have two scopes in Java: 1) class scope, or 2) method scope. You're asking, why not allow another scope -- scope of a local block of code (in this case, the inside of a for loop.

Yes, in some cases this would be helpful. And it would be not too hard to add it to the language. However, these are pretty rare cases, and is more likely to confuse people than help. Also, a relatively simple and effective workaround exists, as you have already discovered -- move that local block scope to a private helper function, which can then use a method-scope type variable:

public void resetFoos(Iterable<Foo<?>> foos) {
    for (Foo<?> foo : foos) {
        resetFoo(foo);
    }
}

private <T> void resetFoo(Foo<T> foo) {
    foo.setValue(foo.getValue());
}

Yes, this might make the code less efficient by making extra method calls, but that is a minor concern.