Java 仿制药-?使用

Java 仿制药-?使用,java,generics,Java,Generics,java.util.Stream接口的JDK文档中有以下代码片段作为收集器构造的示例 Collector<Widget, ?, TreeSet<Widget>> intoSet = Collector.of(TreeSet::new, TreeSet::add, (left, right) -> { left.addAll(right); return left; }); 收集器intoSet= Col

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

Collector<Widget, ?, TreeSet<Widget>> intoSet =
         Collector.of(TreeSet::new, TreeSet::add,
                      (left, right) -> { left.addAll(right); return left; });
收集器intoSet=
Collector.of(TreeSet::new,TreeSet::add,
(左,右)->{left.addAll(右);返回左;});
Collector.of方法的返回类型为
static Collector

是吗?在本文中,示例中的返回类型只是引用下一个泛型类型的一种方便方式,因为这两个泛型类型在方法签名中声明为相同的。并确保以下三个语句是相同的:

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; });
收集器intoSet=
Collector.of(TreeSet::new,TreeSet::add,
(左,右)->{left.addAll(右);返回左;});
收集器intoSet=
Collector.of(TreeSet::new,TreeSet::add,
(左,右)->{left.addAll(右);返回左;});
收集器intoSet=
Collector.of(TreeSet::new,TreeSet::add,
(左,右)->{left.addAll(右);返回左;});

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

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

最后,请注意还有一个:

static <T,A,R> Collector<T,A,R> of
的静态收集器
方法,因此可以有所有不同的类型。

什么是
通配符
? 在通用代码中,问号(
)称为通配符, 表示未知类型。通配符可用于多种应用程序 情况:作为参数、字段或局部变量的类型; 有时作为返回类型(尽管这是更好的编程实践) 更具体地说)。通配符永远不会用作类型参数 对于泛型方法调用,泛型类实例创建,或 超型

为什么它与的收集器一起使用? 正如您已经注意到的
Collector.of(供应商-供应商、双消费者累加器、二进制运算符组合器、特征…特征)
返回类型为
Collector
的对象,知道类
Collector
有3个类型参数,分别是
T
A
R
,其中:

  • :还原操作的输入元素类型
  • :还原操作的可变累积类型(通常作为实现细节隐藏
  • :还原操作的结果类型
因此,如javadoc中所述,我们通常对类型参数
使用通配符,因为它被视为实现细节,因为它只是一个中间类型,真正重要的是输入和输出类型参数分别为
T
R
,因此为了简单性/可读性
优于理论上应该使用的参数,即在这种情况下的
TreeSet

是吗?在本例中,返回类型只是一种方便的方式 引用下一个泛型类型,因为这两个类型已声明为 在方法签名中也是如此

不,是吗?与下一个泛型类型无关。在这个具体的例子中,我们知道吗?恰好是R。这称为类型推断,它是从“=”后面的语句推断出来的。你肯定可以定义一个收集器在哪里?引用与上一个泛型类型不同的类型

并确保以下三个语句是相同的:

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; });
是的,就intoSet的真正含义而言。 不,就如何进一步使用intoSet而言

别让语法把你弄糊涂了。这与

String str = "hello";
vs

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

泛型也是如此。Java没有声明站点差异,但使用站点差异。通配符“?”与Collector.of的定义方式无关,而是与您希望如何使用它有关

这里的
Collector intoSet=…
告诉您,它是一个将小部件收集到TreeSet中的收集器。但它如何将小部件收集到树集中呢?更具体地说,它是直接将每个小部件放入集合中,还是使用中间累加器,例如
堆栈
,并最终将最终结果转换为树集?如果收集器接口定义为
Collection
,则两者都是可能的。然而,intoSet声明只是说“我不在乎,这完全取决于你如何初始化我”

您甚至可以将其定义为
收集器
,完全可以

至于两者的区别

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.of(TreeSet::new,TreeSet::add,
(左,右)->{left.addAll(右);返回左;});
收集器intoSet=
Collector.of(TreeSet::new,TreeSet::add,
(左,右)->{left.addAll(右);返回左;});
它再次涉及到您希望如何使用intoSet。在大多数情况下,我们只关心什么是输入和输出——因为输入是我们需要传入的类型,而输出是我们最终将使用的类型。这两种类型告诉您有关收集器将如何与其余代码交互的关键信息。因此,您可能希望/必须指定它们

收集器int
 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>