阅读完这个问题之后,我开始思考Java 8中的通用方法。具体而言,当方法被链接时,泛型类型参数会发生什么。
对于这个问题,我将使用Guava的
考虑
如果我们使用这种通用方法声明一个
我知道从Java 8开始,编译器的推断机制已经得到了改进,即它可以从上下文中推断方法的泛型类型,这种情况下通常是一个赋值操作。
上下文也可以是一个方法调用:
在这种情况下,
但是当我尝试使用
错误是:
所以我的问题不是如何解决/绕过这个问题,而是为什么在链接泛型方法时需要显式提供通用类型参数。换句话说,为什么编译器不能推断出方法链中的通用类型参数,特别是当方法链位于赋值的右侧时?如果可能的话,这会破坏其他东西吗(我指与通用类型系统相关的东西)?
编辑:
有一个问同样的问题,然而它唯一的答案没有清楚地解释为什么编译器不推断方法链中的通用类型参数。它只有一个对JavaTM编程语言最终版的Lambda表达式进行评估的JSR-000335 Lambda Expressions for the JavaTM Programming Language Final Release for Evaluation(规范部分D)的小段引用。
有人对允许推理“链式”(inference to "chain")表示了兴趣:在a().b()中,将b的调用中的类型信息传递给a的调用。这将增加推理算法的复杂度,因为部分信息必须双向传递;仅当a()的返回类型的擦除对于所有实例化都固定时才有效(例如List)。这个特性不太适合于poly表达式模型,因为目标类型不能很容易地推导出来;但也许在未来可以通过额外的增强来添加它。
所以我认为我可以重新表述我的原始问题如下:
对于这个问题,我将使用Guava的
ImmutableMap
中的一些通用方法,但我的问题更普遍,适用于所有链接的通用方法。考虑
ImmutableMap.of
通用方法,它具有以下签名:public static <K, V> ImmutableMap<K, V> of(K k1, V v1)
如果我们使用这种通用方法声明一个
Map
,编译器会正确推断出通用类型:Map<String, String> map = ImmutableMap.of("a", "b");
我知道从Java 8开始,编译器的推断机制已经得到了改进,即它可以从上下文中推断方法的泛型类型,这种情况下通常是一个赋值操作。
上下文也可以是一个方法调用:
void someMethod(Map<String, String> map) {
// do something with map
}
someMethod(ImmutableMap.of("a", "b"));
在这种情况下,
ImmutableMap.of
的泛型类型是从someMethod
的参数的泛型类型中推断出来的,即Map<String, String> map
。但是当我尝试使用
ImmutableMap.builder()
并链接方法来构建我的映射时,我会得到一个编译错误:Map<String, String> map = ImmutableMap.builder()
.put("a", "b")
.build(); // error here: does not compile
错误是:
Error:(...) java: incompatible types:
ImmutableMap<Object, Object> cannot be converted to Map<String, String>
为了清晰起见,我已经从错误消息中删除了包名。
我理解这个错误以及它发生的原因。链中的第一个方法是ImmutableMap.builder()
,编译器没有上下文来推断类型参数,因此它回退到<Object,Object>
。然后,使用参数"a"
和"b"
调用ImmutableMap.Builder.put
方法,最后调用ImmutableMap.Builder.build()
方法,返回一个ImmutableMap<Object,Object>
。这就是为什么我收到不兼容类型的错误:当我尝试将这个ImmutableMap<Object,Object>
实例分配给我的Map<String,String> map
变量时,编译器会抱怨。
我甚至知道如何解决这个错误:我可以将该方法链拆成两行,这样编译器就可以推断出泛型类型参数:
ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
Map<String, String> map = builder.put("a", "b").build();
或者我可以明确提供泛型类型参数:
Map<String, String> map = ImmutableMap.<String, String>builder()
.put("a", "b")
.build();
所以我的问题不是如何解决/绕过这个问题,而是为什么在链接泛型方法时需要显式提供通用类型参数。换句话说,为什么编译器不能推断出方法链中的通用类型参数,特别是当方法链位于赋值的右侧时?如果可能的话,这会破坏其他东西吗(我指与通用类型系统相关的东西)?
编辑:
有一个问同样的问题,然而它唯一的答案没有清楚地解释为什么编译器不推断方法链中的通用类型参数。它只有一个对JavaTM编程语言最终版的Lambda表达式进行评估的JSR-000335 Lambda Expressions for the JavaTM Programming Language Final Release for Evaluation(规范部分D)的小段引用。
有人对允许推理“链式”(inference to "chain")表示了兴趣:在a().b()中,将b的调用中的类型信息传递给a的调用。这将增加推理算法的复杂度,因为部分信息必须双向传递;仅当a()的返回类型的擦除对于所有实例化都固定时才有效(例如List)。这个特性不太适合于poly表达式模型,因为目标类型不能很容易地推导出来;但也许在未来可以通过额外的增强来添加它。
所以我认为我可以重新表述我的原始问题如下:
- 这些额外的增强会是什么?
- 为什么在双向传递部分信息会使推理算法更加复杂?
- 为什么只有在a()的返回类型的擦除对于所有实例化都固定时才有效(例如List)?事实上,方法的返回类型的擦除对于所有实例化都固定意味着什么?
- 为什么这个特性不太适合于poly表达式模型?实际上,什么是poly表达式模型?为什么在这种情况下目标类型不能很容易地推导出来?