将java泛型错误与映射和收集器混淆

将java泛型错误与映射和收集器混淆,java,eclipse,generics,java-8,java-stream,Java,Eclipse,Generics,Java 8,Java Stream,不久前,我发现了以下有关使用Java 8初始化映射的更干净方法的信息: 使用这些指导原则,我在一个应用程序中实现了以下类: public class MapUtils { public static <K, V> Map.Entry<K, V> entry(K key, V value) { return new AbstractMap.SimpleEntry<>(key, value); } public stati

不久前,我发现了以下有关使用Java 8初始化映射的更干净方法的信息:

使用这些指导原则,我在一个应用程序中实现了以下类:

public class MapUtils {
    public static <K, V> Map.Entry<K, V> entry(K key, V value) {
        return new AbstractMap.SimpleEntry<>(key, value);
    }

    public static <K, U> Collector<Map.Entry<K, U>, ?, Map<K, U>> entriesToMap() {
        return Collectors.toMap((e) -> e.getKey(), (e) -> e.getValue());
    }

    public static <K, U> Collector<Map.Entry<K, U>, ?, ConcurrentMap<K, U>> entriesToConcurrentMap() {
        return Collectors.toConcurrentMap((e) -> e.getKey(), (e) -> e.getValue());
    }
}
公共类MapUtils{
公共静态映射。条目(K键,V值){
返回新的AbstractMap.SimpleEntry(键,值);
}
公共静态收集器entriesToMap(){
返回Collectors.toMap((e)->e.getKey(),(e)->e.getValue());
}
公共静态收集器entriesToConcurrentMap(){
返回Collectors.toConcurrentMap((e)->e.getKey(),(e)->e.getValue());
}
}
在该应用程序中,我实现了如下代码:

public Map<String, ServiceConfig>   serviceConfigs() {
    return Collections.unmodifiableMap(Stream.of(
            entry("ActivateSubscriber", new ServiceConfig().yellowThreshold(90).redThreshold(80)),
            entry("AddAccount", new ServiceConfig().yellowThreshold(90).redThreshold(80).rank(3)),
            ...
            ).
            collect(entriesToMap()));
}
公共地图服务配置(){
返回集合。不可修改映射(Stream.of(
条目(“ActivateSubscriber”,new ServiceConfig().yellowThreshold(90).redThreshold(80)),
条目(“AddAccount”,new ServiceConfig().yellowThreshold(90).redThreshold(80).rank(3)),
...
).
收集(entriesToMap());
}
这段代码运行得非常好

在另一个应用程序中,我将MapUtils类复制到一个包中,并以与在另一个应用程序中相同的方式在一个类中导入该类

我输入以下内容以引用此内容:

        Map<String, USLJsonBase>    serviceRefMap   =
    Collections.unmodifiableMap(Stream.of(
            entry("CoreService", coreService),
            entry("CreditCheckService", creditCheckService),
            entry("PaymentService", paymentService),
            entry("AccountService", accountService),
            entry("OrdercreationService", orderCreationService),
            entry("ProductAndOfferService", productAndOfferService),
            entry("EquipmentService", equipmentService),
            entry("EvergentService", evergentService),
            entry("FraudCheckService", fraudCheckService)
            ).
            collect(entriesToMap()));
Map服务refmap=
集合。不可修改的映射(Stream.of(
条目(“CoreService”,CoreService),
条目(“CreditCheckService”,CreditCheckService),
条目(“PaymentService”,PaymentService),
条目(“AccountService”,AccountService),
条目(“OrdercreationService”,OrdercreationService),
条目(“ProductAndOfferService”,ProductAndOfferService),
条目(“设备服务”,设备服务),
条目(“EvergentService”,EvergentService),
条目(“FraudCheckService”,FraudCheckService)
).
收集(entriesToMap());
在“对方付费”电话中,Eclipse告诉我以下内容:

The method collect(Collector<? super Map.Entry<String,? extends USLJsonBase>,A,R>) in the type Stream<Map.Entry<String,? extends USLJsonBase>> is not applicable for the arguments (Collector<Map.Entry<Object,Object>,capture#1-of ?,Map<Object,Object>>)

方法collect(Collector问题在于,
Stream.of(…).collect(…)
是一个方法调用链,目标类型不会通过这样的链传播。因此,当您将结果分配给参数化的
映射
时,
收集
调用会考虑这些类型参数(和嵌套的
entriesToMap()
调用),但不适用于(…)
调用的

因此,为了推断通过(…)
的流创建的流的类型,只考虑参数的类型

Map<String,Integer> map = Stream.of(entry("foo", 42), entry("bar", 100))
                                .collect(entriesToMap());
Map<String,Number> map = Stream.of(entry("foo", 42L), entry("bar", 100))
                               .collect(entriesToMap());
不起作用,因为推断的流类型是
,收集器不接受该类型来生成
映射

这就引出了一个解决方案,可以放松收集器的通用签名

public static <K, U>
Collector<Map.Entry<? extends K, ? extends U>, ?, Map<K, U>> entriesToMap() {
    return Collectors.toMap((e) -> e.getKey(), (e) -> e.getValue());
}
使用起来更简单:

Map<String,Integer> map1 = mapOf(entry("foo", 42), entry("bar", 100));
Map<String,Number>  map2 = mapOf(entry("foo", 42), entry("bar", 100));
Map<String,Number>  map3 = mapOf(entry("foo", 42L), entry("bar", 100));
Map map1=mapOf(条目(“foo”,42),条目(“bar”,100));
Map map2=mapOf(条目(“foo”,42),条目(“bar”,100));
Map map3=mapOf(条目(“foo”,42L),条目(“bar”,100));

请注意,由于此用法仅包含嵌套调用(无链),目标类型推断在整个表达式中都起作用,即在没有工厂方法的通用签名中的
?extends
的情况下也能起作用。但是为了最大的灵活性,仍然建议使用这些通配符。

可以先将流分配给变量,或者添加类型提示。或者,在Java 9中,使用
Map.of
。在dupe中检查我的答案(我不确定它是否是dupe;将从实际计算机重新打开)。那里有一个Javac标志,它将打开类型推断算法的扩展调试信息。这可能会给出区别的线索。你知道如何让Eclipse使用该标志吗?@DavidM.Karr。你可以设置的所有内容都在RunAs->Run Configuration中。关于你对工作类型的猜测失败的情况下,工作情况下所有的值都具有相同的类型,在失败的情况下,所有的值都是map-value类型的子类的实例。
public static <K, U>
Collector<Map.Entry<? extends K, ? extends U>, ?, Map<K, U>> entriesToMap() {
    return Collectors.toMap((e) -> e.getKey(), (e) -> e.getValue());
}
public static <K, V> Map.Entry<K, V> entry(K key, V value) {
    return new AbstractMap.SimpleImmutableEntry<>(key, value);
}

@SafeVarargs
public static <K, V> Map<K,V> mapOf(Map.Entry<? extends K, ? extends V>... entries) {
    return Stream.of(entries)
             .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

@SafeVarargs
public static <K, V> ConcurrentMap<K,V> concurrentMapOf(
                                        Map.Entry<? extends K, ? extends V>... entries) {
    return Stream.of(entries)
             .collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue));
}
Map<String,Integer> map1 = mapOf(entry("foo", 42), entry("bar", 100));
Map<String,Number>  map2 = mapOf(entry("foo", 42), entry("bar", 100));
Map<String,Number>  map3 = mapOf(entry("foo", 42L), entry("bar", 100));