为什么 Java 泛型类型推断在链式方法调用中中断?
Why does Java generics type inference break in chained method calls?
查看下面示例中泛型类型的类型推断,我不能说为什么 methodAutoTypeInference
工作正常,但 methodNotCompilable
(几乎相同)无法编译和管理它编译器需要额外的技巧,如 methodWorkaroundTypeHint
或 methodWorkaroundTypeCast
.
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();
}
这是编译器的功能。当您在没有 return 语句和方法 of() 的泛型类型的情况下编写 Stream.of("a");
时,编译器从方法参数“a”(Stream<String>
) 获取类型.
当您编写 return Stream.of("a");
时,编译器会从 return 类型 (Stream<CharSequence>
) 获取类型,因为您没有设置泛型类型。
当您编写 return Stream.of("a").distinct();
编译器时,编译器从方法的参数中获取类型(Stream<String>
也是),用于调用 .distinct()
或 .sorted()
换句话说,您没有为方法 of() 设置泛型类型,当您不设置时,编译器会从方法的 return 类型中获取它'没有另一个流的方法,如果你有其他流的方法,则从变量的类型中获取类型。
你会发现这种行为相当多。尤其是在链接 Comparators
时,因为编译器根本无法推断出链下的适当类型。您的解决方案如下。
Stream<CharSequence> methodWorkaroundTypeHint() {
return Stream.<CharSequence>of("a").distinct();
}
被称为type witnessing
,是处理这些问题时的典型解决方案。查看 this answer 了解更多详情。
This answer from JDK Developers themselves 覆盖了相同的区域。请注意 Stuart Marks 说:“编译器可能会得到增强以在未来的版本中涵盖这种情况”。虽然那里的情况大约是 lambdas
,但这与您的情况并没有太大不同。这只是编译器(目前)的工作方式。我们被这个“困住了”。
您可以通过以下方式查看编译器如何考虑 return Stream.of("a").distinct();
并决定使用哪种类型的解决方案:
javac --debug=verboseResolution=all
这是一个未记录的标志。如果你用那个标志编译,你会看到一些大输出:
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
我想最重要的部分是:(partially instantiated to: (String)Stream<String>)
可以看出T
是什么类型的解析,是做方法调用的依据;不是整个调用链。如果会的话,顺便说一句,这会使编译器的工作变得复杂。对于像这样的简单链条,事情可能看起来微不足道,但当有很多事情时,它就会变得非常棘手和复杂。特别是,当您发现 non-denotable types
时,这会使事情变得更加复杂。
查看下面示例中泛型类型的类型推断,我不能说为什么 methodAutoTypeInference
工作正常,但 methodNotCompilable
(几乎相同)无法编译和管理它编译器需要额外的技巧,如 methodWorkaroundTypeHint
或 methodWorkaroundTypeCast
.
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();
}
这是编译器的功能。当您在没有 return 语句和方法 of() 的泛型类型的情况下编写 Stream.of("a");
时,编译器从方法参数“a”(Stream<String>
) 获取类型.
当您编写 return Stream.of("a");
时,编译器会从 return 类型 (Stream<CharSequence>
) 获取类型,因为您没有设置泛型类型。
当您编写 return Stream.of("a").distinct();
编译器时,编译器从方法的参数中获取类型(Stream<String>
也是),用于调用 .distinct()
或 .sorted()
换句话说,您没有为方法 of() 设置泛型类型,当您不设置时,编译器会从方法的 return 类型中获取它'没有另一个流的方法,如果你有其他流的方法,则从变量的类型中获取类型。
你会发现这种行为相当多。尤其是在链接 Comparators
时,因为编译器根本无法推断出链下的适当类型。您的解决方案如下。
Stream<CharSequence> methodWorkaroundTypeHint() {
return Stream.<CharSequence>of("a").distinct();
}
被称为type witnessing
,是处理这些问题时的典型解决方案。查看 this answer 了解更多详情。
This answer from JDK Developers themselves 覆盖了相同的区域。请注意 Stuart Marks 说:“编译器可能会得到增强以在未来的版本中涵盖这种情况”。虽然那里的情况大约是 lambdas
,但这与您的情况并没有太大不同。这只是编译器(目前)的工作方式。我们被这个“困住了”。
您可以通过以下方式查看编译器如何考虑 return Stream.of("a").distinct();
并决定使用哪种类型的解决方案:
javac --debug=verboseResolution=all
这是一个未记录的标志。如果你用那个标志编译,你会看到一些大输出:
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
我想最重要的部分是:(partially instantiated to: (String)Stream<String>)
可以看出T
是什么类型的解析,是做方法调用的依据;不是整个调用链。如果会的话,顺便说一句,这会使编译器的工作变得复杂。对于像这样的简单链条,事情可能看起来微不足道,但当有很多事情时,它就会变得非常棘手和复杂。特别是,当您发现 non-denotable types
时,这会使事情变得更加复杂。