首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >何时在Java泛型中使用通配符?

何时在Java泛型中使用通配符?
EN

Stack Overflow用户
提问于 2013-05-23 06:49:21
回答 6查看 23.8K关注 0票数 42

这来自HeadFirst :(第575页)

这是:

代码语言:javascript
运行
复制
public <T extends Animal> void takeThing(ArrayList<T> list)

做同样的事情:

代码语言:javascript
运行
复制
public void takeThing(ArrayList<? extends Animal> list)

所以我的问题是:如果它们完全相同,我们为什么不写

代码语言:javascript
运行
复制
public <? extends Animal> void takeThing(ArrayList<?> list)

代码语言:javascript
运行
复制
public void takeThing(ArrayList<T extends Animal> list)

另外,什么时候使用一个?用泛型代替方法声明中的T(如上面所示),还是用于类声明?有什么福利待遇?

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2013-05-23 07:27:06

最大的区别是

代码语言:javascript
运行
复制
public <T extends Animal> void takeThing(ArrayList<T> list)

代码语言:javascript
运行
复制
public void takeThing(ArrayList<? extends Animal> list)

在前一种方法中,可以将方法中的"T“称为给定的具体类。在第二种方法中,您不能这样做。

这里有一个更复杂的例子来说明这一点:

代码语言:javascript
运行
复制
// here i can return the concrete type that was passed in
public <T extends Animal> Map<T, String> getNamesMap(ArrayList<T> list) {
    Map<T, String> names = new HashMap<T, String>();
    for (T animal : list) {
        names.put(animal, animal.getName()); // I assume there is a getName() method
    }
    return names;
}

// here i have to use general Animal
public Map<Animal, String> getNamesMap(ArrayList<? extends Animal> list) {
    Map<Animal, String> names = new HashMap<Animal, String>();
    for (Animal animal : list) {
        names.put(animal, animal.getName()); // I assume there is a getName() method
    }
    return names;
}

使用第一种方法,如果您传入一个猫列表,您将得到一个以Cat为键的Map。第二个方法总是返回一个带有一般动物密钥的Map。

顺便说一下,这是无效的java语法:

代码语言:javascript
运行
复制
public <? extends Animal> void takeThing(ArrayList<?> list)

使用这种形式的泛型方法声明,您必须使用有效的java标识符,而不是"?“。

编辑:

表单"?extends“仅适用于变量或参数类型声明。在通用方法解密中,它必须是“标识符扩展类型”,因为您可以在方法中引用“标识符”。

票数 36
EN

Stack Overflow用户

发布于 2013-05-23 08:00:35

通配符是关于仿制药的协/反方差的。我将试图通过提供一些例子来说明这意味着什么。

基本上是因为对于S和T类型,其中S是T的一个子类型,泛型类型G<S>不是G<T>的有效子类型

代码语言:javascript
运行
复制
List<Number> someNumbers = new ArrayList<Long>(); // compile error

你可以用通配符来补救这个问题。

代码语言:javascript
运行
复制
List<? extends Number> someNumbers = new ArrayList<Long>(); // this works

请注意,你不能把任何东西放进这样的名单里。

代码语言:javascript
运行
复制
someNumbers.add(2L); //compile error

甚至(对许多开发人员来说更令人惊讶):

代码语言:javascript
运行
复制
List<? extends Long> someLongs = new ArrayList<Long>();
someLongs.add(2L); // compile error !!!

我认为这不是详细讨论这个问题的合适地方。我将试图找到一些文章和文件,以更详细地解释这一点。

票数 13
EN

Stack Overflow用户

发布于 2013-05-23 14:19:09

将类型绑定到类型参数可能会更强大,这取决于方法应该做什么。我不知道takeThing应该做什么,但是想象一下,一般情况下,我们有一个具有以下类型签名的方法:

代码语言:javascript
运行
复制
public <T extends Animal> void foo(ArrayList<T> list);

//or

public void foo(ArrayList<? extends Animal> list);

下面是一个具体的示例,说明您只能使用第一个类型签名:

代码语言:javascript
运行
复制
public <T extends Animal> void foo(ArrayList<T> list) {
    list.add(list.remove(0)); // (cycle front element to the back)
} 

在这种情况下,需要T通知类型检查器,从列表中删除的元素是要添加到列表中的OK元素。

您不能使用通配符来实现这一点,因为通配符没有绑定到类型参数,因此它的上下文不会被跟踪(通过“捕获”来跟踪它,但是无法利用它)。您可以在我给出的另一个答案中获得更多有关这方面的信息:仿制药是如何工作的?

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16707340

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档