看完之后这个问题 https://stackoverflow.com/questions/43869399/initialize-an-immutablemap-with-explicit-types,我开始考虑泛型方法在Java 8中。具体来说,当方法被链接时,泛型类型参数会发生什么。
对于这个问题,我将使用 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
, ie. 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>
。然后,ImmutableMap.Builder.put
使用参数调用方法"a"
and "b"
最后是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();
所以我的问题不是如何解决/解决这个问题,而是为什么我需要在链接泛型方法时显式提供泛型类型参数。或者,换句话说,为什么编译器不能推断方法链中的泛型类型参数,特别是当方法链位于赋值的右侧时?如果可能的话,这会破坏其他东西吗(我的意思是,与泛型类型系统相关)?
EDIT:
有一个问同样的问题 https://stackoverflow.com/q/24794924/1876620,但是它的唯一答案并没有清楚地解释为什么编译器不推断方法链中的泛型类型参数。它所包含的只是对其中一小段的引用JSR-000335 JavaTM 编程语言最终版本的 Lambda 表达式用于评估 https://jcp.org/aboutJava/communityprocess/final/jsr335/index.html(规范 D 部分):
人们对允许“链”推理感兴趣:在 a().b() 中,将类型信息从 b 的调用传递到 a 的调用。这又增加了推理算法的复杂性,因为部分信息必须在两个方向上传递;仅当所有实例化(例如 List)的 a() 返回类型的擦除都是固定的时,它才起作用。此功能不太适合多表达式模型,因为目标类型无法轻松导出;但也许通过其他增强功能,将来可以添加它。
所以我想我可以将我原来的问题改写如下:
- 这些额外的增强功能是什么?
- 为什么在两个方向上传递部分信息会使推理算法变得更加复杂?
- 为什么只有当所有实例化(例如 List)的 a() 返回类型的擦除都是固定的时,这才有效?事实上,对于所有实例化来说,方法的返回类型的擦除都是固定的,这意味着什么?
- 为什么这个功能不能很好地适应多表达模型?实际上,什么是多表达模型?为什么在这种情况下不能轻松导出目标类型?