Java不应该允许变量声明的泛型类型声明的任何原因?

假设我们有一个这样的类:

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());
    }
}

它将无法编译,即使它看起来像“应该”:

xx.java:10: setValue(capture#496 of ?) in xx.Foo<capture#496 of ?> cannot be applied to (java.lang.Object)
        foo.setValue(foo.getValue());

原因是foo没有绑定泛型类型,因此编译器不“知道”foo.getValue()的输出与foo.setValue()的输入兼容.

所以要解决这个问题,你必须创建一个新方法,只是为了在for()循环中绑定泛型类型参数:

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());
    }
}

这一直让我恼火.此外,似乎可以有一个简单的解决方案.

我的问题:为什么不应该扩展java语言以允许变量声明的泛型类型声明有什么“好”的理由?例如:

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());
        }
    }
}

或者,在这种情况下更简洁的for()循环:

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());
    }
}

我想知道是否有一些编译器向导可以解释为什么这会太难,或者可以(并且应该)完成.

编辑:

针对此建议的解决方案:

public <T> void resetFoos(Iterable<Foo<T>> foos) {
    for (Foo<T> foo : foos) {
        foo.setValue(foo.getValue());
    }
}

此方法签名不允许将具有各种泛型类型的Foos重置在一起.换句话说,尝试传入Iterable< Foo<?>>导致编译错误.

This example演示了这个问题:

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");
}

编译错误如下:

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

(通过ideone.com使用sun-jdk-1.7.0_10)

最佳答案
基本上,类型变量目前只能在Java中有两个范围:1)类范围,或2)方法范围.你问,为什么不允许另一个范围 – 本地代码块的范围(在这种情况下,for循环的内部).

是的,在某些情况下,这会有所帮助.将它添加到语言中并不太难.然而,这些是非常罕见的情况,并且更有可能混淆人而不是帮助.此外,正如您已经发现的那样,存在一个相对简单有效的解决方法 – 将本地块作用域移动到私有辅助函数,然后可以使用方法作用域类型变量:

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

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

是的,这可能会通过进行额外的方法调用来降低代码的效率,但这只是一个小问题.

转载注明原文:Java不应该允许变量声明的泛型类型声明的任何原因? - 代码日志