简体   繁体   English

泛型 - ? 采用

[英]Generics - ? use

JDK documentation of java.util.Stream interface has the following code snippet as an example of Collector construction. java.util.Stream接口的JDK文档具有以下代码片段作为收集器构造的示例。

Collector<Widget, ?, TreeSet<Widget>> intoSet =
         Collector.of(TreeSet::new, TreeSet::add,
                      (left, right) -> { left.addAll(right); return left; });

The Collector.of method has the return type as static <T,R> Collector<T,R,R> Collector.of方法的返回类型为static <T,R> Collector<T,R,R>

Does the ? 是吗? in the return type in the example is just a convenient way to refer to the next generic type as those two have been declared as same in the method signature. 在示例中的返回类型中,只是一种方便的方式来引用下一个泛型类型,因为这两个类型在方法签名中已声明为相同。 And do all the following three statement are one and the same: 并且所有以下三个陈述都是同一个:

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; });

What is a wildcard ? 什么是wildcard

In generic code, the question mark ( ? ), called the wildcard , represents an unknown type . 在通用代码中,称为wildcard问号( ? )表示未知类型 The wildcard can be used in a variety of situations: as the type of a parameter, field, or local variable; 通配符可用于各种情况:作为参数,字段或局部变量的类型; sometimes as a return type (though it is better programming practice to be more specific). 有时作为返回类型(虽然更好的编程实践更具体)。 The wildcard is never used as a type argument for a generic method invocation, a generic class instance creation, or a supertype. 通配符从不用作泛型方法调用,泛型类实例创建或超类型的类型参数。

Why is it used with Collector.of ? 为什么它与Collector.of

As you have already noticed Collector.of(Supplier<R> supplier, BiConsumer<R, T> accumulator, BinaryOperator<R> combiner, Characteristics... characteristics) returns an object of type Collector<T, R, R> knowing that the class Collector has 3 type parameters which are T , A and R where: 正如您已经注意到Collector.of(Supplier<R> supplier, BiConsumer<R, T> accumulator, BinaryOperator<R> combiner, Characteristics... characteristics)返回类型为Collector<T, R, R>的对象,知道类Collector有3个类型参数,分别是TAR ,其中:

  • <T> : The type of input elements to the reduction operation. <T> :缩小操作的输入元素的类型。
  • <A> : The mutable accumulation type of the reduction operation ( often hidden as an implementation detail ) <A> :还原操作的可变累积类型( 通常隐藏为实现细节
  • <R> : The result type of the reduction operation <R> :缩小操作的结果类型

So as described in the javadoc we generally use a wildcard for the type parameter <A> because it is considered as an implementation detail since it is only an intermediate type, what really matter are the input and output type parameters respectively T and R so for the sake of simplicity/readability ? 因此,如javadoc中所述,我们通常使用通配符作为类型参数<A>因为它被认为是一个实现细节,因为它只是一个中间类型,真正重要的是输入和输出类型参数分别为TR所以简单/可读性的缘故? is preferred to what should theoretically be used which is TreeSet<Widget> in this case. 在这种情况下,理论上应该使用TreeSet<Widget>

In Java generics, the '?' 在Java泛型中,'?' is a pure wildcard that will match any type, regardless of any other types in the signature (and there is no syntax for "the next generic type"). 是一个纯通配符,它​​将匹配任何类型,无论签名中是否有任何其他类型(并且没有“下一个泛型类型”的语法)。

Remember that, due to erasure, the types specified in the signature are for compile-time checking only, not for run-time. 请记住,由于擦除,签名中指定的类型仅用于编译时检查,而不是用于运行时。 That means that those three statements will all execute exactly the same. 这意味着这三个语句都将完全相同。 The only difference between them will be if you want generic (compile-time) type checking for the different types. 它们之间的唯一区别是,如果您想要对不同类型进行通用(编译时)类型检查。

Finally, note that there is also a : 最后,请注意还有一个:

static <T,A,R> Collector<T,A,R> of

method, so there could be all different types. 方法,所以可能有所有不同的类型。

Does the ? 是吗? in the return type in the example is just a convenient way to refer to the next generic type as those two have been declared as same in the method signature. 在示例中的返回类型中,只是一种方便的方式来引用下一个泛型类型,因为这两个类型在方法签名中已声明为相同。

No, the ? 不,? has nothing to do with next generic type. 与下一个泛型类型无关。 In this specific example, we know ? 在这个具体的例子中,我们知道吗? happens to be R. This is called type inference, and it is inferred from the statement after the '='. 碰巧是R.这叫做类型推断,它是从'='之后的语句中推断出来的。 You could surely define a Collector where ? 你肯定可以定义一个收藏家在哪里? refers to a different type than the last generic type. 指的是与最后一个通用类型不同的类型。

And do all the following three statement are one and the same: 并且所有以下三个陈述都是同一个:

Yes, in terms of what intoSet really is. 是的,就什么是toSet来说。 No, in terms of how intoSet can be further used 不,就如何进一步使用intoSet而言

Don't let the grammar confuse you. 不要让语法混淆你。 This is just similar to 这与之类似

String str = "hello";

vs.

Object str = "hello";

In both cases, str is a string in runtime. 在这两种情况下,str都是运行时的字符串。 This has nothing to do with how it is declared. 这与它的声明方式无关。 But in the second case, you cannot use str as string, the compiler won't allow you because str is only declared as Object. 但在第二种情况下,你不能使用str作为字符串,编译器不会允许你因为str只声明为Object。

Same thing with generics. 仿制药也是如此。 Java does not have declare-site variance but use-site variance. Java没有声明站点差异,但使用站点差异。 The wildcard "?" 通配符“?” has nothing to do with how Collector.of is defined, but is all about how you want to use it. 与Collector.of的定义无关,而是与你如何使用它有关。

Here Collector<Widget, ?, TreeSet<Widget>> intoSet = ... tells you it is a collector that collects Widget into TreeSet. 这里Collector<Widget, ?, TreeSet<Widget>> intoSet = ...告诉你它是一个收集器,它将Widget收集到TreeSet中。 But how does it collect Widget into a TreeSet? 但它如何将Widget收集到TreeSet中? More specifically, does it directly put each Widget into the set, or it uses an intermediate accumulator, eg a Stack<Widget> , and eventually transforms the final result into TreeSet? 更具体地说,它是直接将每个Widget放入集合中,还是使用中间累加器,例如Stack<Widget> ,并最终将最终结果转换为TreeSet? Both of them are possible given Collector interface is defined as Collection<T,A,R> . 鉴于Collector接口定义为Collection<T,A,R> ,它们都是可能的。 However, the intoSet declaration just says " I don't care , it is all up to how you initialize me". 但是,toSet声明只是说“ 我不在乎 ,这完全取决于你如何初始化我”。

You could even define it as Collector<?, ?, ?> , totally fine. 您甚至可以将其定义为Collector<?, ?, ?> ,完全没问题。

As for the difference between 至于之间的区别

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; });

It once again goes to how you would like to use intoSet. 它再次涉及到你想如何使用toSet。 For most of the time, we only care what is the input and output - for that input is the type we need to pass in, and output is the type we will eventually make use of. 在大多数情况下,我们只关心输入和输出是什么 - 对于那个输入是我们需要传入的类型,输出是我们最终将使用的类型。 These two types tell a critical information about how the collector will interact with the rest of your code. 这两种类型告诉关于收集器如何与其余代码交互的关键信息。 Therefore you probably want to/have to specify them. 因此,您可能希望/必须指定它们。

Collector<Widget, TreeSet<Widget>, ?> intoSet is legal but probably less useful in practice. Collector<Widget, TreeSet<Widget>, ?> intoSet是合法的,但在实践中可能不太有用。 This is because when you want to use intoSet, you have no clue what is the type of result container (therefore you cannot use it meaningfully) by just looking at the declaration . 这是因为当你想使用toSet时,你不知道结果容器的类型是什么(因此你不能有意义地使用它) 只需查看声明

Similar example: 类似的例子:

 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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM