首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >仿制药-?使用

仿制药-?使用
EN

Stack Overflow用户
提问于 2016-10-17 02:03:32
回答 3查看 439关注 0票数 3

java.util.Stream接口的JDK文档有以下代码片段作为收集器构造的示例。

代码语言:javascript
复制
Collector<Widget, ?, TreeSet<Widget>> intoSet =
         Collector.of(TreeSet::new, TreeSet::add,
                      (left, right) -> { left.addAll(right); return left; });

Collector.of方法的返回类型为static <T,R> Collector<T,R,R>

是吗?在示例中的返回类型中,引用下一个泛型类型只是一种方便的方法,因为这两个类型在方法签名中声明为相同。下面的三条语句都是一样的:

代码语言:javascript
复制
Collector<Widget, ?, TreeSet<Widget>> intoSet =
             Collector.of(TreeSet::new, TreeSet::add,
                          (left, right) -> { left.addAll(right); return left; });
Collector<Widget, TreeSet<Widget>, TreeSet<Widget>> intoSet =
             Collector.of(TreeSet::new, TreeSet::add,
                          (left, right) -> { left.addAll(right); return left; });
Collector<Widget, TreeSet<Widget>, ?> intoSet =
             Collector.of(TreeSet::new, TreeSet::add,
                          (left, right) -> { left.addAll(right); return left; });
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2016-10-22 15:04:29

什么是wildcard

在泛型代码中,问号(?) (称为wildcard )表示--一个未知类型的。通配符可以在多种情况下使用:作为参数、字段或局部变量的类型;有时作为返回类型(尽管更具体的编程实践更好)。通配符从未用作泛型方法调用、泛型类实例创建或超类型的类型参数。

为什么它与Collector.of一起使用?

正如您已经注意到的,Collector.of(Supplier<R> supplier, BiConsumer<R, T> accumulator, BinaryOperator<R> combiner, Characteristics... characteristics)返回一个类型为Collector<T, R, R>的对象,知道类Collector有3个类型参数,即TAR,其中:

  • <T>:还原操作的输入元素的类型。
  • <A>:还原操作的可变积累类型(通常隐藏为实现细节)
  • <R>:还原操作的结果类型

因此,正如javadoc中所描述的,我们通常使用通配符作为类型参数<A>,因为它被认为是一个实现细节,因为它只是一个中间类型,真正重要的是输入和输出类型参数分别是TR,因此为了简单/可读性起见,在这种情况下首选?而不是理论上应该使用的TreeSet<Widget>

票数 6
EN

Stack Overflow用户

发布于 2016-10-17 03:00:03

在Java泛型中,“?”是一个纯通配符,它将匹配任何类型,而不管签名中的任何其他类型(并且“下一个泛型类型”没有语法)。

请记住,由于擦除,签名中指定的类型仅用于编译时检查,而不是用于运行时。这意味着这三个语句都将执行完全相同的操作。它们之间的唯一区别将是如果您希望对不同类型进行泛型(编译时)类型检查。

最后,请注意,还有:

代码语言:javascript
复制
static <T,A,R> Collector<T,A,R> of

方法,因此可能存在各种不同的类型。

票数 1
EN

Stack Overflow用户

发布于 2016-10-26 18:18:24

是吗?在示例中的返回类型中,引用下一个泛型类型只是一种方便的方法,因为这两个类型在方法签名中声明为相同。

不,那个?与下一个泛型类型无关。在这个具体的例子中,我们知道吗?碰巧是R,这被称为类型推断,它是从'=‘之后的语句中推断出来的。你肯定可以定义一个收藏家在哪里?引用与上一个泛型类型不同的类型。

下面的三条语句都是一样的:

是的,就intoSet到底是什么而言。不,就如何进一步使用intoSet而言

别让语法弄糊涂了。这就像

代码语言:javascript
复制
String str = "hello";

代码语言:javascript
复制
Object str = "hello";

在这两种情况下,str都是运行时中的字符串。这与声明的方式无关。但是在第二种情况下,不能使用str作为字符串,编译器将不允许您,因为str只声明为Object。

仿制药也是如此。Java没有声明-站点差异,而是使用-站点差异。通配符"?“与如何定义Collector.of无关,而与您希望如何使用它有关。

这里,Collector<Widget, ?, TreeSet<Widget>> intoSet = ...告诉您,它是一个将Widget收集到TreeSet中的收集器。但是它如何将Widget收集到TreeSet中呢?更具体地说,它是直接将每个Widget放入集合,还是使用中间累加器(例如Stack<Widget> ),并最终将最终结果转换为TreeSet?这两者都是可能的,因为收集器接口被定义为Collection<T,A,R>。然而,intoSet声明只是说“--我不在乎,这完全取决于您如何初始化我”。

您甚至可以将其定义为Collector<?, ?, ?>,完全可以。

至于…之间的区别

代码语言:javascript
复制
Collector<Widget, TreeSet<Widget>, TreeSet<Widget>> intoSet =
             Collector.of(TreeSet::new, TreeSet::add,
                          (left, right) -> { left.addAll(right); return left; });
Collector<Widget, TreeSet<Widget>, ?> intoSet =
             Collector.of(TreeSet::new, TreeSet::add,
                          (left, right) -> { left.addAll(right); return left; });

这再次说明了您希望如何使用intoSet。在大多数情况下,我们只关心输入和输出是什么--因为输入是我们需要传入的类型,输出是我们最终将使用的类型。这两种类型告诉您有关收集器将如何与其余代码交互的关键信息。因此,您可能需要/必须指定它们。

Collector<Widget, TreeSet<Widget>, ?> intoSet是合法的,但在实践中可能用处不大。这是因为当您想要使用intoSet时,您不知道结果容器的类型(因此不能有意义地使用),只看声明即可。

类似的例子:

代码语言:javascript
复制
 Map<?, ?> map = new HashMap<String, Integer>();
 map.put("a", 1); //compiler complains here when you try to use map, as map is declared as <?,?>, not <String, Integer>
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40077523

复制
相关文章

相似问题

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