【问题标题】:Why is it necessary to specify generic parameters after they have already been specified?为什么必须在指定泛型参数之后再指定它们?
【发布时间】:2015-05-29 05:46:46
【问题描述】:

我有如下界面

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&lt;T extends Bar&gt; 并且 knownFoos 的值是 Foo&lt;T Extends Bar&gt;Foo 因为它的签名而只能是 Foo&lt;T extends Bar&gt;?我可能天真地认为它与类型擦除有关,但我不确定。

附录:

Foo接口。 T 只能是 BarBar 的子类。代码和编译器清楚地说明了这一点。如果我们要尝试以下操作

Foo<T extends Object> fooObj = new Foo<String>();

编译器会告诉你 String 不在绑定 &lt;T extends Bar&gt; 中。所以在编译时,我们知道没有任何Foo 可以不存在于T extends Bar 的范围内。这与 List&lt;T&gt; 示例不同,因为除了扩展 Object 之外,没有绑定到 T,除非您另有指定。

考虑到这一点,如果编译器足够智能,知道Foo 的边界只能是&lt;T extends Bar&gt;,为什么还需要我显式设置边界?

【问题讨论】:

  • 您希望指定什么?
  • Tpublic Foo&lt;T extends Bar&gt; getFoo 中来自哪里?我没有看到为类或方法定义的任何类型参数T
  • @Zymus 此外,T getBar(); 不会编译,除非您将其设为 abstract。在这一点上,我会要求你停下来并发布一个有效的例子。
  • 我错误地将第一个代码块设为class,而不是预期的interface。我已经更新了问题。

标签: java generics


【解决方案1】:

只要您不介意使用不同的语言,您就不必这样做。

其他语言更多地使用称为类型推断的功能,它可以满足您的期望:查看代码并找出其中的内容。这当然要付出一些代价。例如,Scala 以编译时间长和有时令人困惑的情况而闻名,当类型推断不能推断出开发人员应该做的类型时。

对于 Java,几乎所有内容都必须手动指定(尽管现在可以使用 运算符进行类型推断。

你提到的替代方案

Foo

Foo<T extends Bar>

Foo<? extends Bar> 

are in fact different types,您必须明智地选择使用哪个。

【讨论】:

  • 即使 getFoo 只返回 Foo,代码仍然是类型安全的。如果编译器看到 Foo 绑定到扩展某些东西的类型参数,为什么它不简单地允许返回 Foo ? Foo 的任何实现都将其类型参数限制为至少 Bar 或 Bar 本身的子类
【解决方案2】:

为什么需要我指定 getFoo 返回一个 Foo

相对于什么?返回一个隐式使用泛型 Bar 的 Foo?那不一样。

让我们用一个具体的例子:

List<? extends Number> getList();

此方法签名会告诉您它返回 List&lt;Integer&gt;List&lt;Float&gt; 或任何其他 Number 子类型的 List。但是 List 将只包含这一子类型的对象。在这种情况下,通配符运算符用于向调用者表示您不能告诉他使用的是哪个子类型。

这与返回数字列表不同,因为数字列表可以包含任何数字子类型的对象。

这个区别很重要:如果您返回List&lt;Number&gt;,则允许调用者将任何 Number 对象添加到列表中。所以列表会突然包含数字的混合,而以前它只包含整数。使用通配符运算符可以防止这种情况发生,因为编译器无法检查新 Number 的类型是否正确。

编译器检查它很重要。如果不是这样,您可能会在代码的其他部分遇到 ClassCastExceptions,其中 List 与具体泛型一起使用,而不是通配符。

【讨论】:

  • A List 可以是任何泛型类型,具体取决于其类型定义。在这种情况下,Foo 只能是扩展 BarBar 本身的东西。它类似于public interface NumberList&lt;T extends Number&gt;
  • 您似乎认为返回泛型&lt;? extends Bar&gt; 等同于返回泛型&lt;Bar&gt;。虽然这可能适用于您的情况,但这根本不是真的。我使用 List 示例来解释差异。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-08-28
  • 1970-01-01
  • 2018-09-01
  • 1970-01-01
  • 2020-05-23
  • 2012-09-27
  • 1970-01-01
相关资源
最近更新 更多