M. *_*tin 5 java generics lambda compiler-errors unbounded-wildcard
我当前正在使用一个库方法,该方法采用带有通用通配符类型的功能接口作为方法参数(具体来说,在AssertJ库中)。我发现,当我传递使用通配符类型参数以外的任何类型的 lambda 方法参数时,我会收到编译错误。例如,如果该方法是,则当我调用 时会出现编译器错误。RecursiveComparisonAssert.withEqualsForFields\xe2\x80\x8b(BiPredicate<?,\xe2\x80\x8b?> equals, String... fieldLocations)ObjectsameInstant(Instant i1, Instant i2)withEqualsForFields(this::sameInstant, "someField")
作为这种现象的一个更简单的例子,不需要使用任何特定的库来重现,请采用以下使用Predicate<?>方法参数的场景:
public class WildcardLambda {\n public static void main(String[] args) {\n wildcardPredicateInput(WildcardLambda::objectPredicate);\n wildcardPredicateInput(WildcardLambda::stringPredicate); // Fails\n wildcardPredicateInput((Predicate<String>) WildcardLambda::stringPredicate);\n wildcardPredicateInput(input -> stringPredicate(input));\n\n genericPredicateInput(WildcardLambda::objectPredicate);\n genericPredicateInput(WildcardLambda::stringPredicate);\n }\n\n private static void wildcardPredicateInput(Predicate<?> predicate) {}\n private static <T> void genericPredicateInput(Predicate<T> predicate) {}\n\n private static boolean objectPredicate(Object input) { return true; }\n private static boolean stringPredicate(String input) { return true; }\n}\nRun Code Online (Sandbox Code Playgroud)\n尝试将 a 的 lambda 传递给Predicate<String>接受 a 的方法会Predicate<?>导致编译错误:
$ javac WildcardLambda.java -Xdiags:verbose\nWildcardLambda.java:6: error: method wildcardPredicateInput in class WildcardLambda cannot be applied to given types;\n wildcardPredicateInput(WildcardLambda::stringPredicate); // Fails\n ^\n required: Predicate<?>\n found: WildcardLa[...]icate\n reason: argument mismatch; invalid method reference\n method stringPredicate in class WildcardLambda cannot be applied to given types\n required: String\n found: Object\n reason: argument mismatch; Object cannot be converted to String\n1 error\nRun Code Online (Sandbox Code Playgroud)\n但是,传递一个 lambdaPredicate<Object>或显式转换该 lambda 即可Predicate<String>成功。此外,如果将其传递给需要Predicate<T>.
为什么这种 lambda 用法会导致编译错误?JLS中是否有我忽略的内容表明这应该无法编译?
\n小智 4
这是一个已知的类型推断问题。如JLS 18.5.3中所述:
\n\n\n为了确定通配符参数化函数接口的函数类型,我们必须用特定类型“实例化”通配符类型参数。“默认”方法是简单地将通配符替换为其边界,如 \xc2\xa79.8 中所述,但如果 lambda 表达式具有与通配符边界不对应的显式参数类型,则会产生虚假错误。
\n
这里通配符类型参数被“实例化”到其绑定Object,并且因为Predicate<String>不是 的子类型Predicate<Object>,所以该方法被认为不适用。
通过强制转换(如帖子中所示)或局部变量(如下所示)提供类型提示可以解决问题。
\nPredicate<String> myPredicate = WildcardLambda::stringPredicate;\nwildcardPredicateInput(myPredicate);\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
144 次 |
| 最近记录: |