Fed*_*ner 10 java generics methods method-chaining java-8
在阅读完这个问题之后,我开始考虑Java 8中的泛型方法.具体来说,当链接方法时,泛型类型参数会发生什么.
对于这个问题,我将使用Guava的一些通用方法ImmutableMap,但我的问题更为通用,可以应用于所有链式泛型方法.
考虑ImmutableMap.of具有此签名的泛型方法:
public static <K, V> ImmutableMap<K, V> of(K k1, V v1)
Run Code Online (Sandbox Code Playgroud)
如果我们使用这个泛型方法声明a Map,编译器会正确推断泛型类型:
Map<String, String> map = ImmutableMap.of("a", "b");
Run Code Online (Sandbox Code Playgroud)
我知道从Java 8开始,编译器推理机制已得到改进,即它从上下文中推断出通用类型的方法,在本例中是一个赋值.
上下文也可以是方法调用:
void someMethod(Map<String, String> map) {
// do something with map
}
someMethod(ImmutableMap.of("a", "b"));
Run Code Online (Sandbox Code Playgroud)
在这种情况下,泛型类型ImmutableMap.of是从参数的泛型类型推断出来的someMethod,即.Map<String, String> map.
但是当我尝试使用ImmutableMap.builder()和链接方法来构建我的地图时,我得到一个编译错误:
Map<String, String> map = ImmutableMap.builder()
.put("a", "b")
.build(); // error here: does not compile
Run Code Online (Sandbox Code Playgroud)
错误是:
Error:(...) java: incompatible types:
ImmutableMap<Object, Object> cannot be converted to Map<String, String>
Run Code Online (Sandbox Code Playgroud)
(为清楚起见,我已从错误消息中删除了软件包名称).
我理解错误及其发生的原因.链中的第一个方法是ImmutableMap.builder(),并且编译器没有用于推断类型参数的上下文,因此它回退到<Object, Object>.然后,ImmutableMap.Builder.put方法被调用以参数"a"和"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();
Run Code Online (Sandbox Code Playgroud)
或者我可以显式提供泛型类型参数:
Map<String, String> map = ImmutableMap.<String, String>builder()
.put("a", "b")
.build();
Run Code Online (Sandbox Code Playgroud)
所以我的问题不是如何解决/解决这个问题,而是为什么我需要在链接泛型方法时显式提供泛型类型参数.或者,换句话说,为什么编译器不能推断方法链中的泛型类型参数,尤其是当方法链位于赋值的右侧时?如果有可能,这会破坏其他东西(我的意思是,与泛型类型系统有关)?
编辑:
有一个问题要求相同,但是它唯一的答案并没有清楚地解释为什么编译器不会推断方法链中的泛型类型参数.它只是对JSR-000335 Lambda表达式中用于JavaTM编程语言评估最终版本(规范D部分)的小段落的引用:
允许推理"链"有一些兴趣:在a().b()中,将类型信息从b的调用传递给a的调用.这为推理算法的复杂性增加了另一个维度,因为部分信息必须在两个方向上传递; 它只适用于a()的返回类型的擦除对于所有实例化(例如List)是固定的.此特征不适合多聚表达模型,因为目标类型不能轻易导出; 但也许还有其他增强功能,可以在将来添加.
所以我想我可以将我原来的问题改写如下:
如果这确实应该是一条评论,请告诉我,我会将其分成单独的评论(它可能不适合单个评论)。
第poly expression一个是在不同的上下文中可以有不同的类型。你声明里面contextA有一些东西typeA;一进去contextB就有了typeB。
ImmutableMap.of("a", "b") 您通过或 来 创建地图所做的ImmutableMap.of(1,2)就是这样的事情。更准确地说,JLS 中的第 15.2 章说这实际上是一个类实例创建表达式Poly 表达式。
到目前为止我们已经确定了A generic class instance creation is a poly expression。因此,实例创建可以根据使用它的上下文而具有不同的类型(这显然会发生)。
现在,在您的示例中,ImmutableMap.builder事情并不难推断(如果您可以链接builder,of例如) Builder 的声明如下:
public static <K, V> Builder<K, V> builder() {
return new Builder<K, V>();
}
Run Code Online (Sandbox Code Playgroud)
像ImmutableMap.of这样:
public static <K, V> ImmutableMap<K, V> of() {
return ImmutableBiMap.of();
}
Run Code Online (Sandbox Code Playgroud)
请注意两者如何声明相同的泛型类型K,V。这可能很容易从一种方法传递到另一种方法。let's say 10 methods但请考虑当您的方法 ( ) 每个声明不同的泛型类型界限时的情况,例如:
<K,V> of()
....
<M extends T, R extends S> build()
Run Code Online (Sandbox Code Playgroud)
等等。你可以把它们拴起来。有关类型的信息必须来回left to right传递right to left才能进行推理,据我所知,这很难做到(除了非常复杂之外)。
现在,它的工作方式是,根据我所看到的,每个多表达式一次编译一个(并且您的示例似乎证明了这一点)。