Java何时需要显式类型参数? [英] When does Java require explicit type parameters?
问题描述
鉴于:
import com.google.common.collect.ImmutableMap;
import java.util.function.Function;
import java.util.stream.Collector;
import java.util.stream.Stream;
public class Testcase
{
public static <T, K, V> MapCollectorBuilder<T, K, V>
toImmutableMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends V> valueMapper)
{
return null;
}
public static final class MapCollectorBuilder<T, K, V>
{
public Collector<T, ?, ImmutableMap<K, V>> build()
{
return null;
}
}
public static <T, K, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableMap2(
Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends V> valueMapper)
{
return null;
}
public void main(String[] args)
{
Function<String, String> keyMapper = i -> i;
Function<String, Integer> valueMapper = Integer::valueOf;
ImmutableMap<String, Integer> map1 = Stream.of("1", "2", "3")
.collect(Testcase.toImmutableMap(keyMapper, valueMapper).build());
ImmutableMap<String, Integer> map2 = Stream.of("1", "2", "3")
.collect(Testcase.toImmutableMap(i -> i, Integer::valueOf).build());
ImmutableMap<String, Integer> map3 = Stream.of("1", "2", "3")
.collect(Testcase.toImmutableMap2(i -> i, Integer::valueOf));
}
}
涉及 map1的语句
和 map3
编译正常,但 map2
失败:
The statements involving map1
and map3
compile fine, but map2
fails with:
Testcase.java:[41,57] incompatible types: cannot infer type-variable(s) T,K,V
(argument mismatch; invalid method reference
no suitable method found for valueOf(java.lang.Object)
method java.lang.Integer.valueOf(java.lang.String) is not applicable
(argument mismatch; java.lang.Object cannot be converted to java.lang.String)
method java.lang.Integer.valueOf(int) is not applicable
(argument mismatch; java.lang.Object cannot be converted to int))
编译器错误可以通过提供显式类型参数来解决< String,String,Integer>
。
The compiler error can be solved by providing explicit type parameters <String, String, Integer>
.
- Java 8何时需要显式类型参数?意思是,是否存在打破类型推断的已知模式?
- 可以
toImmutableMap()
和MapCollectorBuilder
是否可以更改以避免使用显式类型参数而不会丢失使用Builder来配置收集器?
- When does Java 8 require explicit type parameters? Meaning, is there a known pattern that breaks type inference?
- Can
toImmutableMap()
andMapCollectorBuilder
be changed to avoid explicit type parameters without losing the use of a Builder for configuring the Collector?
更新:
- 为什么声明涉及
map3 $ c $工作?它与涉及
map2
的声明有何不同?
- Why does the statement involving
map3
work? How does it differ from the statement involvingmap2
?
推荐答案
回答你的问题意义,是否有一种打破类型推断的已知模式?很快:当然,有一种模式,而且还有一个巨大的规范,用于Java编程语言的整个行为。
To answer your question "Meaning, is there a known pattern that breaks type inference?" shortly: of course, there is a pattern, moreover there is a huge specification for the entire behavior of the Java programming language.
但是关于类型推断和方法调用类型的章节非常详尽且难以理解。最好的说明是,在出现意外行为的情况下,通常会根据规范对预期行为进行大量讨论。
But the chapters regarding type inference and method invocation types are really exhaustive and hard to understand. This is best illustrated by the fact that in the case of unexpected behavior, often large discussions about the expected behavior according to the specification occur.
但有一些点可以解释,对程序员来说是值得记住的。
But there are some points explainable and rememberable for a programmer.
有两种方法可以推断出类型参数,通过传递给方法的参数或构成表达式的部分,或者表达式的目标类型,即调用参数的预期类型,已分配的变量或返回语句时方法的返回类型。
There are two ways how to infer the type parameters, by the arguments passed to a method or parts from which an expression is composed or by the target type of an expression, that is, the expected type for a parameter of an invocation, the variable which is assigned or the return type of a method in case of a return statement.
目标类型可以通过嵌套方法调用传播,例如
The target type can get propagated through nested method invocations like in
TargetType x=foo(bar(/*target type can be used*/));
或有条件的
TargetType x=condition? foo(/*target type can be used*/): foo(/*target type can be used*/);
但不如果是链式调用
TargetType x=foo(/*target type can NOT be used*/).foo();
现在举例来说:
Now to your examples:
ImmutableMap<String, Integer> map1 = Stream.of("1", "2", "3").collect( expression );
这里, Stream.of(...)
和 .collect(...)
是链接,因此目标类型不能用于确定的流类型
调用,但提供给该方法的参数足以推断出类型 Stream< String>
。 collect
方法提供了分配给 map1
的结果,因此两者都是流类型流< String>
和目标类型 ImmutableMap< String,Integer>
已知且可用于该类型推断表达式。在表达式上:
Here, the Stream.of(…)
and .collect(…)
are chained, therefore the target type cannot be used to determine the stream type of the of
invocation but the arguments provided to that method are sufficient to infer the type Stream<String>
. The collect
method provides the result that gets assigned to map1
, so both, the stream type Stream<String>
and the target type ImmutableMap<String, Integer>
are known and can be used for the type inference for the expression. Onto the expressions:
-
Testcase.toImmutableMap(keyMapper,valueMapper).build()
这是一个链式调用,因此目标类型为build()
而已知,但不是toImmutableMap
。但是,toImmutableMap
的参数是具有已知确切类型的局部变量,因此类型推断可以使用它们来推断toImmutableMap <的结果类型/ code>并检查它是否符合
.build()的预期
Testcase.toImmutableMap(keyMapper, valueMapper).build()
this is a chained invocation, so the target type is known forbuild()
but not fortoImmutableMap
. However, the arguments totoImmutableMap
are local variables which have a known exact type, therefore the type inference can use them to infer the result type oftoImmutableMap
and check whether it matches the expectations for.build()
Testcase.toImmutableMap(i - > i,Integer :: valueOf).build()
这又是一个链式调用,但现在参数 i - > ;我
的类型不完整,并且缺少目标类型。尝试猜测的类型i - >我
不知道目标类型失败。
Testcase.toImmutableMap(i -> i, Integer::valueOf).build()
this is again a chained invocation but now the argument i - > i
has an incomplete type and suffers from the absence of the target type. The attempt to guess a type for i -> i
without knowledge about the target type fails.
Testcase.toImmutableMap2(i - > i, Integer :: valueOf)
这是不链式调用,因此目标类型可用于 toImmutableMap2
调用(对于 collect
调用,它是一个嵌套调用)。因此, toImmutableMap2
的目标类型允许推断参数的目标类型,因此对于 i - >我
lambda表达式。使用正确的目标类型,可以推断出正确的功能签名。
Testcase.toImmutableMap2(i -> i, Integer::valueOf)
this is not a chained invocation, therefore the target type is available for the toImmutableMap2
call (in respect to the collect
call, it’s a nested invocation). Therefore, the target type of toImmutableMap2
allows to infer target types for the parameters, hence for the i -> i
lambda expression. With a proper target type, the correct functional signature can be inferred.
这篇关于Java何时需要显式类型参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!