Java 仿制药-?使用
java.util.Stream接口的JDK文档中有以下代码片段作为收集器构造的示例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
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
,其中:
:还原操作的输入元素类型
:还原操作的可变累积类型(通常作为实现细节隐藏)
:还原操作的结果类型
使用通配符,因为它被视为实现细节,因为它只是一个中间类型,真正重要的是输入和输出类型参数分别为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>