如何限定Java方法引用参数类型解决Comparator编译报错
以下代码无法正常编译:
Set<String> premiumStrings = new HashSet<>(); Set<String> sortedSet = new TreeSet<>(Comparator.comparing(premiumStrings::contains).thenComparing(Comparator.naturalOrder()));
报错信息如下:
The method thenComparing(Comparator<? super Object>) in the type Comparator<Object> is not applicable for the arguments (Comparator<Comparable<? super Comparable<? super T>>>)
根因是Set.contains方法的签名为boolean contains(Object o),因此直接使用premiumStrings::contains作为方法引用时,编译器会将Comparator.comparing返回的比较器推断为Comparator<Object>类型,后续链式调用thenComparing时,传入的Comparator.naturalOrder()是针对Comparable类型的比较器,和Comparator<Object>的类型要求不匹配,最终编译失败。
如果不想把方法引用改写为(String s) -> premiumStrings.contains(s)形式的Lambda表达式,有两种保留方法引用写法的可行方案:
方案1:显式指定Comparator.comparing的泛型参数
直接为Comparator.comparing方法显式声明泛型类型,强制限定比较的元素类型为String,不需要修改方法引用本身:
Set<String> sortedSet = new TreeSet<>( Comparator.<String, Boolean>comparing(premiumStrings::contains) .thenComparing(Comparator.naturalOrder()) );
这种写法直接给编译器传递了明确的类型信息:当前比较器的入参类型是String,比较键的类型是Boolean,后续thenComparing的泛型会顺着上下文正确推断为String,和naturalOrder()的类型要求完全匹配。
方案2:用显式类型的函数式接口变量承接方法引用
先将方法引用赋值给明确声明入参类型为String的Function变量,再将该变量传入Comparator.comparing:
Function<String, Boolean> premiumChecker = premiumStrings::contains; Set<String> sortedSet = new TreeSet<>( Comparator.comparing(premiumChecker) .thenComparing(Comparator.naturalOrder()) );
由于premiumChecker已经被明确声明为「接收String类型入参、返回Boolean类型结果」的函数,编译器做类型推断时不会再将入参类型放宽为Object,整个比较器的类型链可以正常匹配。
两种方案都完全保留了方法引用的写法,本质都是为编译器补充足够的类型信息,避免因contains方法的宽泛入参导致泛型推断出错。
内容的提问来源于stack exchange,提问作者H.v.M.

