Java中的所有通配符都可以替换为非通配符类型吗?

发布于 2024-12-03 06:36:09 字数 502 浏览 2 评论 0原文

我找不到任何通配符不能被泛型替换的例子。 例如:

 public void dummy(List<? extends MyObject> list);

等于

 public <T> void dummy(List<T extends MyObject> list);

 public <T> List<? extends T> dummy2(List<? extends T> list);

等于

 public <T, U>  List<U extends T> dummy(List<U extends T> list);

所以我不明白为什么创建通配符,因为泛型已经完成了这项工作。有什么想法或意见吗?

I can't find any example where wildcards can't be replaced by a generic.
For example:

 public void dummy(List<? extends MyObject> list);

is equivalent to

 public <T> void dummy(List<T extends MyObject> list);

or

 public <T> List<? extends T> dummy2(List<? extends T> list);

is equivalent to

 public <T, U>  List<U extends T> dummy(List<U extends T> list);

So I don't undertand why wildcard was created as generics is already doing the job. Any ideas or comments?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

茶色山野 2024-12-10 06:36:09

不,它并不总是可以更换的。

List<? extends Reader> foo();

不等于,

<T> List<T extends Reader> foo();

因为您在调用 foo() 时不知道 T (并且您无法知道 List 将< 中。

code>foo() 返回。同样的事情也发生在你的第二个例子 href="https://softwareengineering.stackexchange.com/questions/103710/what-has-been-learned-about-making-variance-part-of-the-type/106026#106026">使用通配符的演示可以在此(我的)答案中找到。

Nope, it is not always replaceable.

List<? extends Reader> foo();

is not equivalent to

<T> List<T extends Reader> foo();

because you don't know the T when calling foo() (and you cannot know what List<T> will foo() return. The same thing happens in your second example, too.

The demonstration of using wildcards can be found in this (my) answer.

白鸥掠海 2024-12-10 06:36:09

一个简单的答案是,更深层次的通配符不能被类型变量替换

void foo( List<List<?>> arg )

<T> 
void foo( List<List<T>> arg)

这是因为通配符捕获转换仅适用于第一级通配符。我们来谈谈这些吧。

由于广泛的捕获转换,在大多数地方,编译器将通配符视为类型变量。因此,程序员确实可以在这些地方用类型变量替换通配符,这是一种手动捕获转换。

由于编译器为捕获转换创建的类型变量不可被程序员访问,因此这具有@josefx提到的限制效果。例如,编译器将 List 对象视为 List 对象;由于 W 是编译器内部的,尽管它有一个方法 add(W item),但程序员无法调用它,因为他没有 W。但是,如果程序员“手动”将通配符转换为类型变量 T,他就可以拥有类型为 T 的项目并调用 add(T item)就可以了。

通配符无法替换类型变量的另一个相当随机的情况:

class Base
    List<? extends Number> foo()

class Derived extends Base
    List<Integer> foo()

这里,foo() 被协变返回类型覆盖,因为 ListList。如果通配符被类型变量替换,这将不起作用。

An easy answer is that, deeper level wildcards can't be replaced by a type variable

void foo( List<List<?>> arg )

is very different from

<T> 
void foo( List<List<T>> arg)

This is because wildcard capture conversion is only applied to 1st level wildcards. Let's talk about these.

Due to extensive capture conversion, in most places, compiler treats wildcards as if they are type variables. Therefore indeed programmer can replace wildcard with type variables in such places, a sort of manual capture conversion.

Since a type variable created by compiler for capture conversion is not accessible to programmer, this has the restricting effect mentioned by @josefx. For example, compiler treats a List<?> object as a List<W> object; since W is internal to compiler, although it has a method add(W item), there's no way for programmer to invoke it, because he has no item of type W. However, if programmer "manually" converts the wildcard to a type variable T, he can have an item with type T and invoke add(T item) on it.

Another rather random case where wildcard can't be replaced type variable:

class Base
    List<? extends Number> foo()

class Derived extends Base
    List<Integer> foo()

Here, foo() is overriden with a covariant return type, since List<Integer> is a subtype of List<? extends Number. This won't work if the wildcard is replaced with type variable.

作业与我同在 2024-12-10 06:36:09

您的示例存在用法差异。

public <T> List<? extends T> dummy2(List<? extends T> list);

返回一个列表,其中可以包含 T 的未知子类型,因此您可以从中获取类型 T 的对象,但无法添加到其中。

示例 T = Number

List<? extends Number> l = new ArrayList<Integer>(Arrays.asList(new Integer(1)));
//Valid since the values are guaranteed to be a subtype of Number
Number o = l.get(0);
//Invalid since we don't know that the valuetype of the list is Integer
l.add(new Integer(1));

因此通配符可用于使某些操作无效,API 可以使用它来限制接口。

例子:

//Use to remove unliked numbers, thanks to the wildcard 
//it is impossible to add a Number
@Override public void removeNumberCallback(List<? extends Number> list){
    list.remove(13);
}

There is a usage difference for your examples.

public <T> List<? extends T> dummy2(List<? extends T> list);

returns a list that can contain an unknown subtype of T so you can get objects of type T out of it but can't add to it.

Example T = Number

List<? extends Number> l = new ArrayList<Integer>(Arrays.asList(new Integer(1)));
//Valid since the values are guaranteed to be a subtype of Number
Number o = l.get(0);
//Invalid since we don't know that the valuetype of the list is Integer
l.add(new Integer(1));

So the wildcard can be used to make some operations invalid, this can be used by an API to restrict an interface.

Example:

//Use to remove unliked numbers, thanks to the wildcard 
//it is impossible to add a Number
@Override public void removeNumberCallback(List<? extends Number> list){
    list.remove(13);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文