public class Foo<T extends Bar>{
private Class<T> _type;
public Foo( Class<T> _type ){
this._type = _type;
}
public Collection<T> hypothetical( List<T> items ){ //PROBLEMATIC
return dostuffWithItems( items );
}
}
用法:
Foo<? extends ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
List<ChildBar> items = ( List<ChildBar> ) foo.hypothetical( new ArrayList<ChildBar>() ); //COMPILER ERROR: The method hypothetical(List<capture#2-of ?>) in the type Foo<capture#2-of ?> is not applicable for the arguments (List<ChildBar>)
编译器会接受
向List<ChildBar> items
投射List<?>
参数
或将hypothetical( List<T> items )
签名更改为
a)hypothetical( List<ChildBar> items )
或
b)hypothetical( List<? extends Bar> items )
但是,没有一个替代方案可以确保假设方法的List items参数T type是Foo类T参数类型的等效运行时类型。我目前正在使用额外的方法来验证参数类型。
Java泛型构造中是否有更好的方法可以自动实现这一点,而无需额外的逻辑?或者更好的是,为什么我不能将foo声明为Foo<? extends Bar>
然后在运行时填写实际的类型参数?
答案 0 :(得分:5)
我编辑了你的代码并添加了缺少的东西以使其可编辑,我可以确认唯一有问题的部分是:
dostuffWithItems
方法。hypothetical
方法名称的拼写错误。Collection<ChildBar>
分配给List<ChildBar>
。前两个很容易修复。
最后一个要求您更改API方法的更改,或更改您调用它的代码。这些都不是(IMO)有问题的。此外,
值得注意的是,如果类型是非泛型的,您将获得所有这些错误。您无法在没有类型转换的情况下将Collection
分配给List
。
这是我的代码供你玩。 (复制并粘贴到适当命名的文件中......)
public class Bar {
}
public class ChildBar extends Bar {
}
import java.util.*;
public class Foo<T extends Bar> {
private Class<T> _type;
public Foo( Class<T> _type ) {
this._type = _type;
}
public Collection<T> hypothetical( List<T> items ) {
return items; // dummy implementation ...
}
}
import java.util.*;
public class Main {
public static void main(String[] args) {
Foo<ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
Collection<ChildBar> items =
foo.hypothetical( new ArrayList<ChildBar>() );
}
}
答案 1 :(得分:2)
接受的答案并没有准确解释为什么问题中的代码段(编辑后)会被编译器拒绝。
我们从观察到@Stephen C的答案的片段被接受开始,而问题的第8版被拒绝。区别在于:在后一版本中,变量foo
使用通配符参数化类型Foo<? extends ChildBar>
声明,而Stephen C从早期版本复制Foo<ChildBar>
(我们似乎都同意这是解决编译错误的合适方法。)
要了解为什么这种差异至关重要,请参阅Foo<? extends ChildBar> foo
这个通配符作为捕获传播到foo.hypothetical
的调用签名,因此此调用呈现为hypothetical(List<capture#2-of ?>)
,意味着该参数具有未知(上限)类型参数。 List<ChildBar>
与该类型不兼容,因此编译错误。
还要注意所有提到&#34;运行时&#34;在这个线程中是不合适的,所有这些都是在编译时静态解决的。也许你的意思是调用类型或实际参数的类型,而不是声明的类型(形式参数)。编译器不知道实际的运行时类型。
答案 2 :(得分:-2)
这在Java中似乎是不可能的。
Foo<? extends ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
这使得foo具有模糊的参数类型。很明显,ChildBar将成为真正的事实上的参数类型。使用foo.hypothetical()
调用List<ChildBar>
方法会使此假设不真实。虽然foo.hypothetical
只接受包含foo参数类型元素的List<>
参数,但它仍然无法识别该参数是ChildBar对象的列表。
对于此用例,必须在foo声明期间指定对象参数类型,以使其成为foo运行时引用的一部分。
Foo<ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
现在,foo.hypothetical方法的所有符合List<ChildBar>
参数都将被正确接受为foo声明的参数类型的元素。