Java 8 流中的转换类型

2022-09-02 02:17:32

为了获得一些Java新流的经验,我一直在开发一个处理扑克牌的框架。这是我的代码的第一个版本,用于创建一个包含一手牌中每套牌数的代码(是一个):MapSuitenum

Map<Suit, Long> countBySuit = contents.stream() // contents is ArrayList<Card>  
        .collect( Collectors.groupingBy( Card::getSuit, Collectors.counting() ));

这效果很好,我很高兴。然后我重构,为“套装卡”和小丑创建单独的卡子类。所以这个方法从类移到了它的子类,因为小丑没有西装。新代码:getSuit()CardSuitCard

Map<Suit, Long> countBySuit = contents.stream() // contents is ArrayList<Card>  
        .filter( card -> card instanceof SuitCard ) // reject Jokers
        .collect( Collectors.groupingBy( SuitCard::getSuit, Collectors.counting() ) );

请注意巧妙地插入过滤器,以确保所考虑的卡实际上是西装卡而不是小丑。但它不起作用!显然,该行没有意识到它所传递的对象保证是 .collectSuitCard

在对此感到困惑了很长一段时间之后,在绝望中,我尝试插入一个函数调用,令人惊讶的是它的工作原理!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() ) );

我不知道强制转换类型被认为是可执行语句。为什么会这样?为什么编译器使它变得必要?


答案 1

请记住,操作不会更改 的 s 元素的编译时类型。是的,从逻辑上讲,我们看到所有使它超过这一点的东西都将是一个,所看到的只是一个。如果该谓词稍后更改,则可能导致其他编译时问题。filterStreamSuitCardfilterPredicate

如果要将其更改为 ,则需要添加一个映射器,用于执行强制转换: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以获取完整的详细信息。


答案 2

好吧,允许将 a 转换为 using 函数,该函数采用 as 参数并返回 .和map()Stream<Foo>Stream<Bar>FooBar

card -> (SuitCard) card

就是这样一个函数:它采用一张 Card 作为参数并返回一张 SuitCard。

如果你愿意,你可以这样写,也许这会让你更清楚:

new Function<Card, SuitCard>() {
    @Override
    public SuitCard apply(Card card) {
        SuitCard suitCard = (SuitCard) card;
        return suitCard;
    }
}

编译器认为这是必要的,因为 filter() 将 a 转换为 .因此,您不能将仅接受 SuitCard 的函数应用于该流的元素,该流可能包含任何类型的 Card:编译器不关心筛选器的作用。它只关心它返回的类型。Stream<Card>Stream<Card>


推荐