在 Java 8 流中转换类型

新手上路,请多包涵

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

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

这很好用,我很高兴。然后我重构,为“Suit Cards”和 Jokers 创建单独的 Card 子类。所以 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() ) );

请注意过滤器的巧妙插入,以确保所考虑的卡片实际上是花色卡片而不是小丑。但它不起作用!显然 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() ) );

我不知道转换类型被认为是可执行语句。为什么这行得通?为什么编译器需要它?

原文由 Robert Lewis 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 505
2 个回答

请记住 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 以获取完整的详细信息。

原文由 Joe C 发布,翻译遵循 CC BY-SA 3.0 许可协议

Well, map() allows transforming a Stream<Foo> into a Stream<Bar> using a function that takes a Foo as argument and returns a Bar 。和

card -> (SuitCard) card

就是这样一个函数:它接受一个 Card 作为参数并返回一个 SuitCard。

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

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

编译器使它变得必要,因为 filter() 将 Stream<Card> 转换为 Stream<Card> 。因此,您不能将仅接受 SuitCard 的函数应用于该流的元素,该流可能包含任何类型的 Card:编译器不关心您的过滤器的作用。它只关心它返回什么类型。

原文由 JB Nizet 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题