简体   繁体   English

在Java 8流中使用自定义收集器时获取ClassCastException

[英]Getting a ClassCastException when using a custom Collector with java 8 streams

I have a custom Collector of the sort 我有一个自定义的Collector

public class ClusteringCollector extends java.util.stream.Collector<MyModel, Map<String, ClusterModel>, SortedSet<Map.Entry<Integer, ClusterModel>>> {
    @Override
    public Supplier<Map<String, MyOtherModel>> supplier() {
        return HashMap::new;
    }

    @Override
    public BiConsumer<Map<String, ClusterModel>, MyModel> accumulator() {
        return (l, r) -> {
            String mapKey = r.getURI();
            if(l.containsKey(mapKey)) {
                l.get(mapKey).addCluster(r.getCluster());
            } else {
                l.put(mapKey, r.getCluster());
            }
        }
    }

    @Override
    public BinaryOperator<Map<String, ClusterModel>> combiner() {
        return (left, right) -> {
            for(Map.Entry<String, ClusterModel> e : right.entrySet()) {
                e.getValue().setClusterCount(1);
                if(left.containsKey(e.getKey())) {
                    left.get(e.getKey()).merge(e.getValue());
                } else {
                    left.put(e.getKey(), e.getValue());
                }
            }

            return left;
        };
    }

    @Override
    public Function<Map<String, ClusterModel>, SortedSet<Map.Entry<Integer, ClusterModel>>> finisher() {
        return (accumulated) -> {
            SortedSet<Map.Entry<Integer, ClusterModel>> finished = new TreeSet<>((mine, theirs) -> {

               Double t1 = mine.getValue().getClusterCount() * mine.getValue().getClusterWeight();
               Double t2 = theirs.getValue().getClusterCount() * theirs.getValue().getClusterWeight();

               return t2.compareTo(t1);
            });

            Map<Integer, ClusterModel> tempMap = new LinkedHashMap<>();
            for(Map.Entry<String, ClusterModel> e : accumulated.entrySet()) {
                if(tempMap.containsKey(e.getValue().hashCode())) {
                    tempMap.get(e.getValue().hashCode()).merge(e.getValue());
                } else {
                    tempMap.put(e.getValue().hashCode(), e.getValue());
                }
            }

            finished.addAll(tempMap.entrySet());

            return finished;
        };
    }

    @Override
    public Set<Characteristics> characteristics() {
        return EnumSet.of(Characteristics.UNORDERED, Characteristics.IDENTITY_FINISH);
    }
}

I use the collector in the following way 我通过以下方式使用收集器

try (Stream<MyModel> resultStream = generateDataStream()) {
    SortedSet<Map.Entry<Integer, ClusterModel>> clusters = resultStream.collect(new ClusteringCollector()); // This line throws a ClassCastException
}

The problem though is that I keep getting a ClassCastException when I try to run the collect method above. 问题是,当我尝试运行上面的collect方法时,我一直收到ClassCastException Here is the stacktrace 这是堆栈跟踪

java.lang.ClassCastException: java.util.HashMap cannot be cast to java.util.SortedSet
    com.mycomp.abc.core.services.DefaultClusteringServiceImpl.findClusters(DefaultClusteringServiceImpl.java:78)
    com.mycomp.abc.core.webservices.ClusteringWebService.getClustersFromQuery(ClusteringWebService.java:67)
    com.mycomp.abc.core.webservices.ClusteringWebService$Proxy$_$$_WeldClientProxy.getClustersFromQuery(Unknown Source)
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    java.lang.reflect.Method.invoke(Method.java:498)
    org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:137)
    org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:296)
    org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:250)
    org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:237)
    org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:356)
    org.jboss.resteasy.core.SynchronousDispatcher.invokePropagateNotFound(SynchronousDispatcher.java:217)
    org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:224)
    org.jboss.resteasy.plugins.server.servlet.FilterDispatcher.doFilter(FilterDispatcher.java:62)

Can someone tell me why this is happening? 有人可以告诉我为什么会这样吗? I am not getting any compilation errors though and the finisher transforms the Map into a SortedSet correctly. 我没有收到任何编译错误,并且finisherMap正确转换为SortedSet

There are several errors in the posted code which suggest that this is not the actual code, however, the main problem is recognizable. 发布的代码中存在多个错误,表明这不是实际的代码,但是主要问题是可以识别的。 You specified the IDENTITY_FINISH characteristic, despite you have a complex conversion in the finisher. 尽管在装订器中进行了复杂的转换,但您仍指定了IDENTITY_FINISH特性。

The IDENTITY_FINISH characteristic implies that the finisher was just like Function.identity() , but at runtime, the Stream implementation can't check whether the generic signatures are compatible with that declaration. IDENTITY_FINISH特性暗示整理器就像Function.identity() ,但是在运行时,Stream实现无法检查泛型签名是否与该声明兼容。 When it uses this characteristic to decide to skip the finisher, it will just return the container object, which is a HashMap in your case, which, of course, is not assignable to SortedSet . 当它使用此特性决定跳过装订器时,它将仅返回容器对象,在您的情况下是HashMap ,当然,该对象不能分配给SortedSet

In the end, that's the better outcome of this mistake. 最后,这是此错误的更好结果。 The worse would be if the container and result type are compatible and the skipping of a nontrivial finisher stays unnoticed at first. 更糟糕的是,如果容器和结果类型兼容,并且起初没有忽略不重要的修整器的跳过。 So be careful about specifying the IDENTITY_FINISH characteristic. 因此,在指定IDENTITY_FINISH特征时要小心。

Note that when you don't implement Collector , but rather construct one by passing the functions to Collector.of(…) , you never need to specify that characteristic, as it will be injected based on whether you specified a finisher function or not. 请注意,当您不实现Collector ,而是通过将函数传递给Collector.of(…)来构造一个,则无需指定该特征,因为将根据您是否指定了finisher函数来注入该特征。 For the overloaded method without a finisher, the generic signature will even ensure that the container type matches the result type. 对于没有整理程序的重载方法,泛型签名甚至可以确保容器类型与结果类型匹配。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM