Arc*_*hie 8 java generics language-design
假设我们有一个这样的类:
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());
}
}
Run Code Online (Sandbox Code Playgroud)
它将无法编译,即使直观地看起来它应该"应该":
xx.java:10: setValue(capture#496 of ?) in xx.Foo<capture#496 of ?> cannot be applied to (java.lang.Object)
foo.setValue(foo.getValue());
Run Code Online (Sandbox Code Playgroud)
原因是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());
}
}
Run Code Online (Sandbox Code Playgroud)
这一直让我恼火.此外,似乎可以有一个简单的解决方案.
我的问题:是否有任何"好"的理由为什么不应该扩展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());
}
}
}
Run Code Online (Sandbox Code Playgroud)
或者,在这种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());
}
}
Run Code Online (Sandbox Code Playgroud)
我想知道一些编译器向导是否可以解释为什么这会太难,或者可以(并且应该)完成.
编辑:
针对此建议的解决方案:
public <T> void resetFoos(Iterable<Foo<T>> foos) {
for (Foo<T> foo : foos) {
foo.setValue(foo.getValue());
}
}
Run Code Online (Sandbox Code Playgroud)
此方法签名不允许将Foo具有各种泛型类型的s重置在一起.换句话说,尝试传入一个Iterable<Foo<?>>导致编译错误.
此示例演示了此问题:
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");
}
Run Code Online (Sandbox Code Playgroud)
编译错误如下:
方法
resetFoos不能应用于给定的类型;需要:
Iterable<Foo<T>>发现:
List<Foo<?>>原因:没有类型变量的实例
T存在,因此参数类型List<Foo<?>>符合形式参数类型Iterable<Foo<T>>,其中T是一个类型变量:T extends Object在方法中声明<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());
}
Run Code Online (Sandbox Code Playgroud)
是的,这可能会通过额外的方法调用来降低代码的效率,但这是一个小问题。
| 归档时间: |
|
| 查看次数: |
1980 次 |
| 最近记录: |