我有以下界面
public interface Foo<T extends Bar> {
T getBar();
}
以下课程
public class FooFinder {
public Foo<? extends Bar> getFoo(final String fooName) {
return knownFoos.get(fooName);
}
private Map<String, Foo<? extends Bar>> knownFoos = new HashMap<>();
}
我的问题是这个。为什么我需要指定getFoo
返回Foo<T extends Bar>
,knownFoos
的值为Foo<T Extends Bar>
,而Foo
只能是Foo<T extends Bar>
因为它的签名?我可能天真地认为它与类型擦除有关,但我不确定。
附录:
选择Foo
界面。 T
只能是Bar
或Bar
的子类。代码和编译器清楚地说明了这一点。如果我们尝试以下
Foo<T extends Object> fooObj = new Foo<String>();
编译器会告诉您String不在绑定<T extends Bar>
中。因此,在编译时,我们知道没有Foo
可以存在,而不是T extends Bar
。这与List<T>
示例不同,因为除了T
之外没有约束Object
,除非您另行指定。
考虑到这一点,如果编译器足够聪明,知道Foo
的范围只能是<T extends Bar>
,为什么还要求我明确设置绑定?
答案 0 :(得分:0)
只要您不介意使用其他语言,就不必这样做。
其他语言更多地使用称为类型推断的功能,它可以满足您的期望:查看代码并找出其中的内容。这当然需要一些代价。例如,Scala以长编译时间和有时令人困惑的情况而闻名,当类型推断没有推断开发人员应该使用的类型时。
使用Java几乎所有东西都必须手动指定(尽管现在使用&lt;&gt;运算符进行了一些类型推断。
您提到的替代方案
Foo
Foo<T extends Bar>
Foo<? extends Bar>
are in fact different types,你必须明智地选择使用哪个。
答案 1 :(得分:0)
为什么我需要指定getFoo返回Foo&lt; ?扩展Bar&gt;
与什么相反?返回一个隐含使用泛型Bar的Foo?那是不一样的。
让我们使用一个具体的例子:
List<? extends Number> getList();
此方法签名会告诉您它返回List<Integer>
或List<Float>
或任何其他Number子类型的List。但List只包含这一个子类型的对象。在这种情况下,通配符运算符用于向调用者表示您无法告诉他使用了哪个子类型。
这与返回数字列表不同,因为数字列表将被允许包含任何子类型为Number的对象。
这种区别非常重要:如果您返回List<Number>
,则允许调用者将任何Number对象添加到List中。所以List会突然包含Numbers的混合,而之前它只包含整数。使用通配符运算符可以防止这种情况,因为编译器无法检查新Number是否具有正确的类型。
编译器检查它很重要。如果它没有,您可能会在代码的其他部分遇到ClassCastExceptions
,其中List与具体泛型而不是通配符一起使用。