方法链接中的泛型参数推断 [英] Generic type parameters inference in method chaining

查看:147
本文介绍了方法链接中的泛型参数推断的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

阅读此问题后,我开始考虑通用Java 8中方法.具体来说,方法链接时,泛型类型参数会发生什么情况.

After reading this question, I've started to think about generic methods in Java 8. Specifically, what happens with generic type parameters when methods are chained.

对于这个问题,我将使用番石榴ImmutableMap中的一些泛型方法,但是我的问题更为笼统,可以应用于所有链接的泛型方法.

For this question, I will use some generic methods from Guava's ImmutableMap, but my question is more general and can be applied to all chained generic methods.

考虑ImmutableMap.of通用方法,该方法具有以下签名:

Consider ImmutableMap.of generic method, which has this signature:

public static <K, V> ImmutableMap<K, V> of(K k1, V v1)

如果使用此通用方法声明Map,则编译器会正确推断出通用类型:

If we use this generic method to declare a Map, the compiler infers the generic types correctly:

Map<String, String> map = ImmutableMap.of("a", "b");

我知道从Java 8开始,编译器推断机制已得到改进,即它可以从上下文中推断方法的通用类型,在这种情况下,这是一种赋值.

I'm aware that from Java 8 onwards, the compiler inference mechanism has been improved, i.e. it infers the generic types of methods from the context, which in this case is an assignment.

上下文也可以是方法调用:

The context can also be a method call:

void someMethod(Map<String, String> map) { 
    // do something with map
}

someMethod(ImmutableMap.of("a", "b"));

在这种情况下,ImmutableMap.of的通用类型是从someMethod参数的通用类型推断的,即. Map<String, String> map.

In this case, the generic types of ImmutableMap.of are inferred from the generic types of the argument of someMethod, ie. Map<String, String> map.

但是当我尝试使用ImmutableMap.builder()和链方法构建地图时,出现编译错误:

But when I attempt to use ImmutableMap.builder()and chain methods to build my map, I get a compilation error:

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>

(为清楚起见,我已从错误消息中删除了软件包名称).

(I've removed the packages names from the error message for the sake of clarity).

我了解该错误以及发生原因.链中的第一个方法是ImmutableMap.builder(),并且编译器没有上下文可以推断类型参数,因此它回退到<Object, Object>.然后,使用参数"a""b"调用ImmutableMap.Builder.put方法,最后调用ImmutableMap.Builder.build()方法,该方法返回ImmutableMap<Object, Object>.这就是为什么我收到不兼容类型错误的原因:当我尝试将此ImmutableMap<Object, Object>实例分配给我的Map<String, String> map变量时,编译器会抱怨.

I understand the error and why it happens. The first method in the chain is ImmutableMap.builder() and the compiler has no context to infer the type parameters, so it fallbacks to <Object, Object>. Then, the ImmutableMap.Builder.put method is invoked with arguments "a" and "b" and finally the ImmutableMap.Builder.build() method is called, which returns an ImmutableMap<Object, Object>. This is why I'm receiving the incompatible types error: when I attempt to assign this ImmutableMap<Object, Object> instance to my Map<String, String> map variable, the compiler complains.

我什至知道如何解决此错误:我可以将方法链分成两行,以便编译器现在可以推断出通用类型参数:

I even know how to solve this error: I could either break the method chain into two lines, so that the compiler can now infer the generic type parameters:

ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
Map<String, String> map = builder.put("a", "b").build();

或者我可以显式提供泛型类型参数:

Or I could explicitly provide the generic type parameters:

Map<String, String> map = ImmutableMap.<String, String>builder()
    .put("a", "b")
    .build();

所以我的问题不是如何解决/解决这个问题,而是为什么在链接泛型方法时确实需要显式提供泛型类型参数.或者换句话说,为什么编译器不能推断方法链中的泛型类型参数,特别是当方法链位于赋值的右侧时?如果可能的话,这会打破其他东西吗(我的意思是,与泛型类型系统有关)?

So my question is not how to solve/workaround this, but why I do need to provide the generic type parameters explicitly when chaining generic methods. Or, in other words, why can't the compiler infer the generic type parameters in a method chain, especially when the method chain is at the right side of an assignment? If it were possible, would this break something else (I mean, related to the generics type system)?

编辑:

有一个问相同的问题,但是它唯一的答案并不能清楚地解释为什么编译器不这样做. t推断方法链中的泛型类型参数.它所具有的只是对 JSR-000335 Lambda表达式的一小段的引用JavaTM编程语言评估最终版(规范D部分):

There's a question asking the same, however the only answer it has doesn't clearly explain why the compiler doesn't infer the generic type parameters in a method chain. All it has is a reference to a small paragraph in the JSR-000335 Lambda Expressions for the JavaTM Programming Language Final Release for Evaluation (spec part D):

在允许推理到链"方面引起了一些兴趣:在a().b()中,将类型信息从b的调用传递到a的调用.由于部分信息必须在两个方向上传递,这为推理算法的复杂性增加了另一个维度.仅当所有实例化(例如List)的a()返回类型的擦除固定时,它才起作用.由于无法轻松导出目标类型,因此此功能不太适合多表达模型.但也许有其他增强功能,将来可能会添加.

There has been some interest in allowing inference to "chain": in a().b(), passing type information from the invocation of b to the invocation of a. This adds another dimension to the complexity of the inference algorithm, as partial information has to pass in both directions; it only works when the erasure of the return type of a() is fixed for all instantiations (e.g. List). This feature would not fit very well into the poly expression model, since the target type cannot be easily derived; but perhaps with additional enhancements it could be added in the future.

所以我想我可以将原来的问题改写如下:

So I think I might rephrase my original question as follows:

  • 这些额外的增强功能是什么?
  • 在两个方向上传递部分信息会使推理算法更加复杂吗?
  • 为什么只有在对所有实例化(例如List)固定了a()返回类型的擦除后,这才起作用?实际上,对于所有实例化而言,方法返回类型的擦除是固定的,这意味着什么?
  • 为什么这个功能不能很好地适合多边形表达模型?其实,多边形表达模型是什么?为什么在这种情况下无法轻松导出目标类型?

推荐答案

如果这确实是一条评论,请告诉我,我将其分解为单独的评论(它可能不会适合一个评论).

If this should really be a comment, please let me know and I'll break it in separate comments (it will probably not fit into a single one).

第一个poly expression是在不同上下文中可以具有不同类型的一个.您在contextA中声明具有typeA的内容; contextB中的一个,它具有typeB.

First poly expression is one that can have different types in different contexts. You declare something in contextA it has typeA; one in contextB and it has typeB.

通过ImmutableMap.of("a", "b") ImmutableMap.of(1,2)创建地图所​​做的事情就是这样.更准确地说,第15.2章JLS 说,这实际上是

What you are doing with your map creation via ImmutableMap.of("a", "b") or ImmutableMap.of(1,2) is such a thing. To be more precise Chapter 15.2 in the JLS says that this is actually a Class Instance Creation Expression poly expression.

到目前为止,我们已经确定了A generic class instance creation is a poly expression.因此,实例创建可能会根据使用的上下文而具有不同的类型(显然如此).

So far we have established that A generic class instance creation is a poly expression. So that instance creation could have different types based on the context where it is used (and that obviously happens).

现在在示例中使用ImmutableMap.builder进行推断并没有那么困难(例如,如果您可以链接builderof),生成器的声明如下:

Now in your example with a ImmutableMap.builder things are not that difficult to infer (if you could chain builder and of for example) Builder is declared like this:

   public static <K, V> Builder<K, V> builder() {
      return new Builder<K, V>();
   }

ImmutableMap.of像这样:

   public static <K, V> ImmutableMap<K, V> of() {
      return ImmutableBiMap.of();
   }

注意两者如何声明相同的泛型类型K,V.这可能很容易从一种方法传递到另一种方法.但是请考虑当您的方法(let's say 10 methods)各自声明泛型类型的不同界限时的情况,例如:

Notice how both declare the same generic types K,V. This would probably be easy to pass from one method to another. But consider the case when your methods (let's say 10 methods) each declare different bound of the generic types, like:

<K,V> of()
....
<M extends T, R extends S> build()

以此类推.您可以将它们链接起来.必须从left to rightright to left传递有关类型的信息,才能进行推断,并且据我所知很难做到(除了非常复杂之外).

And so on. And you can chain them. Information about types has to be passed from left to right and from right to left for the inference to work and as far as I can tell it would be very hard to do (besides being very complex).

现在,此方法的工作方式是,根据我所看到的情况,每个poly表达式一次被编译(您的示例似乎证明了这一点).

Right now the way this works is that each poly expression is compiled one at a time from what I see (and your examples seems to prove that).

这篇关于方法链接中的泛型参数推断的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆