为了获得Java新流的一些经验,我一直在开发用于处理纸牌的框架。这是我的代码的第一个版本,该代码用于创建Map
,其中包含手中的每套西装的张数(Suit
是enum
):
Map<Suit, Long> countBySuit = contents.stream() // contents is ArrayList<Card>
.collect( Collectors.groupingBy( Card::getSuit, Collectors.counting() ));
这很好,我很高兴。然后,我进行了重构,为“Suit Cards”和Jokers创建了单独的Card子类。因此,由于Jokers没有合适的衣服,
getSuit()
方法已从Card
类移至其子类SuitCard
。新代码:Map<Suit, Long> countBySuit = contents.stream() // contents is ArrayList<Card>
.filter( card -> card instanceof SuitCard ) // reject Jokers
.collect( Collectors.groupingBy( SuitCard::getSuit, Collectors.counting() ) );
请注意,巧妙地插入了过滤器,以确保所考虑的卡实际上是西装卡,而不是 clown 卡。但这不起作用!显然
collect
行没有意识到要传递的对象已保证是SuitCard
。在对此困惑了好一会儿之后,无奈之下,我尝试插入一个
map
函数调用,并且令人惊讶地成功了!Map<Suit, Long> countBySuit = contents.stream() // contents is ArrayList<Card>
.filter( card -> card instanceof SuitCard ) // reject Jokers
.map( card -> (SuitCard)card ) // worked to get rid of error message on next line
.collect( Collectors.groupingBy( SuitCard::getSuit, Collectors.counting() ) );
我不知道强制类型转换被视为可执行语句。为什么这样做?为何编译器需要这样做?
最佳答案
请记住,filter
操作不会更改Stream
元素的编译时类型。是的,从逻辑上讲,我们可以看到,超过这一点的所有内容都将是SuitCard
,filter
所看到的只是Predicate
。如果该谓词稍后更改,则可能导致其他编译时问题。
如果要将其更改为Stream<SuitCard>
,则需要添加一个为您执行强制转换的映射器:
Map<Suit, Long> countBySuit = contents.stream() // Stream<Card>
.filter( card -> card instanceof SuitCard ) // still Stream<Card>, as filter does not change the type
.map( SuitCard.class::cast ) // now a Stream<SuitCard>
.collect( Collectors.groupingBy( SuitCard::getSuit, Collectors.counting() ) );
详细信息请引用Javadoc。