简体   繁体   English

为什么 Java generics 类型推理在链式方法调用中中断?

[英]Why does Java generics type inference break in chained method calls?

Looking at the type inference for generic types in the following example, I cannot say why methodAutoTypeInference works fine, but methodNotCompilable (which is almost the same) fails to compile and to manage it the compiler requires additional tricks like methodWorkaroundTypeHint or methodWorkaroundTypeCast .查看以下示例中泛型类型的类型推断,我不能说为什么methodAutoTypeInference工作正常,但methodNotCompilable (几乎相同)无法编译和管理它,编译器需要额外的技巧,如methodWorkaroundTypeHintmethodWorkaroundTypeCast

What is the problem with methodNotCompilable causing the compiler to be unsure that the expression type and the method result type are compatible? methodNotCompilable有什么问题导致编译器不确定表达式类型和方法结果类型是否兼容?

Stream<CharSequence> methodAutoTypeInference() {
  return Stream.of("a");
}

Stream<CharSequence> methodNotCompilable() {
  return Stream.of("a").distinct(); 
  //    incompatible types:  java.util.stream.Stream<java.lang.String>
  // cannot be converted to  java.util.stream.Stream<java.lang.CharSequence>
}

Stream<CharSequence> methodWorkaroundTypeHint() {
  return Stream.<CharSequence>of("a").distinct();
}

Stream<CharSequence> methodWorkaroundTypeCast() {
  return Stream.of((CharSequence) "a").distinct();
}

This answer from JDK Developers themselves sort of covers the same area. JDK开发人员自己的这个答案涵盖了相同的领域。 Just notice that Stuart Marks says: "It might be possible for the compiler to be enhanced to cover this case in a future release".请注意 Stuart Marks 说:“编译器可能会得到增强,以在未来的版本中涵盖这种情况”。 Though the case there is around lambdas , this is not very different than you have.尽管lambdas周围有这种情况,但这与您的情况并没有太大不同。 It's just the way compiler (at the moment) works.这只是编译器(目前)的工作方式。 And we are "stuck" with this.我们对此“卡住”了。

You can sort of look under the resolution of how a compiler thinks about return Stream.of("a").distinct();你可以看看编译器如何考虑return Stream.of("a").distinct(); and decides what type to use, via:并通过以下方式决定使用什么类型:

javac --debug=verboseResolution=all

which in an un-documented flag.在未记录的标志中。 If you compile with that flag, you will see some big output:如果你用那个标志编译,你会看到一些大的 output:

  with actuals: no arguments
  with type-args: no arguments
  candidates:
      #0 applicable method found: Object()
DeleteMe.java:60: Note: resolving method of in type Stream to candidate 1
        return Stream.of("a").distinct();
                     ^
  phase: BASIC
  with actuals: String
  with type-args: no arguments
  candidates:
      #0 not applicable method found: <T#1>of(T#1...)
        (cannot infer type-variable(s) T#1
          (argument mismatch; String cannot be converted to T#1[]))
      #1 applicable method found: <T#2>of(T#2)
        (partially instantiated to: (String)Stream<String>)
  where T#1,T#2 are type-variables:
    T#1 extends Object declared in method <T#1>of(T#1...)
    T#2 extends Object declared in method <T#2>of(T#2)
DeleteMe.java:60: Note: Deferred instantiation of method <T>of(T)
        return Stream.of("a").distinct();
                        ^
  instantiated signature: (String)Stream<String>
  target-type: <none>
  where T is a type-variable:
    T extends Object declared in method <T>of(T)
DeleteMe.java:60: Note: resolving method distinct in type Stream to candidate 0
        return Stream.of("a").distinct();
                             ^
  phase: BASIC
  with actuals: no arguments
  with type-args: no arguments
  candidates:
      #0 applicable method found: distinct()
  where T is a type-variable:
    T extends Object declared in interface Stream
DeleteMe.java:60: error: incompatible types: Stream<String> cannot be converted to Stream<CharSequence>
        return Stream.of("a").distinct();
                                      ^
1 error

I guess the most important part is this: (partially instantiated to: (String)Stream<String>)我想最重要的部分是:( (partially instantiated to: (String)Stream<String>)

You can see that the resolution of what type T is, is done a method call basis;可以看到T是什么类型的解析,是在方法调用的基础上做的; not of the whole chain of calls.不是整个调用链。 If it would would, btw, this would complicate compilers works quite a lot.如果可以的话,顺便说一句,这会使编译器的工作变得相当复杂。 For a simple chain like this, things might look trivial, but it gets far, far more tricky and involved when there are many.对于这样一个简单的链,事情可能看起来微不足道,但当有很多时,它会变得更加棘手和复杂。 Especially, when you find out about non-denotable types , which will even further complicate this.尤其是当您发现non-denotable types时,这会使情况更加复杂。

It's a compiler's feature.这是编译器的功能。 When you write Stream.of("a");当你写Stream.of("a"); without return statement and generic type for method of() compiler gets the type from methods parameter "a" ( Stream<String> ).没有返回语句和泛型类型的方法of()编译器从方法参数“a”( Stream<String> )获取类型。

When you write return Stream.of("a");当你写return Stream.of("a"); compiler gets the type from the return type ( Stream<CharSequence> ) because you don't set generic type.编译器从返回类型( Stream<CharSequence> )获取类型,因为您没有设置泛型类型。 And when you write return Stream.of("a").distinct();当你写return Stream.of("a").distinct(); compiler get type from method's parameter ( Stream<String> too), for calling .distinct() or .sorted()编译器从方法的参数中获取类型( Stream<String>也是),用于调用.distinct().sorted()

In other words, you don't set generic type for a method of() , and the compiler got it from the method's return type when you don't have another stream's method, and got type from variable's type if you have other stream's methods.换句话说,您没有为of()方法设置泛型类型,当您没有另一个流的方法时,编译器从方法的返回类型中获取它,如果您有其他流的方法,则从变量的类型中获取类型.

You will find this behavior quite a bit.你会发现这种行为相当多。 Especially when chaining Comparators as the compiler simply can't infer the appropriate types down the chain.尤其是在链接Comparators器时,因为编译器根本无法在链中推断出适当的类型。 Your solution below.您的解决方案如下。

Stream<CharSequence> methodWorkaroundTypeHint() {
        return Stream.<CharSequence>of("a").distinct();
}

Is referred to as type witnessing and is the typical solution when dealing with these issues.被称为type witnessing ,是处理这些问题时的典型解决方案。 Check this answer for more details on this.检查此答案以获取更多详细信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM