java 无法从通过函数引用推导出泛型类型问题

记录今天在写一个工具类时遇到泛型问题:无法从通过函数引用推导出泛型类型

问题复现

工具函数:

public static <K, V, C extends Collection<? super V>> C getKeys(Map<K, ? extends V> map,
                                                                Supplier<? extends C> container,
                                                                Boolean filterNull,
                                                                K... keys) {
    C coll = container.get();
    for (K key : keys) {
        V value = map.get(key);
        if (filterNull && value == null) {
            continue;
        }
        coll.add(value);
    }
    return coll;
}

测试函数:

public static void main(String[] args) {
    Map<String, String> map = Map.of(
             "1", "1",
             "2", "2",
             "3", "3"
     );
     
    System.out.println(getKeys(map, ArrayList::new, true, "1", "2"));
}

idea 编译错误提示:

不存在泛型变量 E 的实例,所以 ArrayList<E> 遵循了 String 类型

idea 编译错误提示

原因说明

根据 idea 的编译错误信息,我们可以发现:最终在调用打印函数时,匹配的是 public void println(String x) 函数,而不是 public void println(Object x)。显然,ArrayList 并不是 String 类型,所以编译器抛出了编译异常。

那么为什么会这样?在看一下 getKeys 函数的泛型声明:

public static <K, V, C extends Collection<? super V>> C getKeys(Map<K, ? extends V> map,
                                                                Supplier<? extends C> container,
                                                                Boolean filterNull,
                                                                K... keys)

类型 C 是根据 Supplier 接口推到出来的,但我们实际传递的是一个函数引用:ArrayList::new。很明显,函数引用中是不存在泛型信息的,这就是问题所在。由于无法推断出泛型类型,所以编译器不知道该如何匹配 println 函数的重载签名。

解决方式

直接、间接的声明泛型类型,使得编译器可以正确推导,并进行函数签名的匹配。

// 1. 不使用函数引用
System.out.println(getKeys(map, () -> new ArrayList<>(), true, "1", "2"));

// 2. 通过设置返回值的泛型类型,传递给 C 泛型类型
ArrayList<String> values = getKeys(map, ArrayList::new, true, "1", "2");
System.out.println(values);
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值