Java 8流中的转换类型

Rob*_*wis 14 java lambda casting java-8

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

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

这很好用,我很高兴.然后我重构,为"Suit Cards"和Jokers创建单独的Card子类.所以这个getSuit()方法从Card类转移到了它的子类SuitCard,因为Jokers没有套装.新代码:

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

请注意巧妙地插入过滤器以确保所考虑的卡实际上是西装卡而不是小丑.但它不起作用!显然,这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() ) );
Run Code Online (Sandbox Code Playgroud)

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

Joe*_*e C 28

请记住,filter操作不会更改Stream元素的编译时类型.是的,从逻辑上讲,我们看到所有超越这一点的东西都是a SuitCard,所有filter看到的都是Predicate.如果该谓词稍后更改,则可能导致其他编译时问题.

如果要将其更改为a 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() ) );
Run Code Online (Sandbox Code Playgroud)

我将您介绍给Javadoc以获取完整的详细信息.

  • 例如,我也更喜欢方法参考方式:`.filter(SuitCard.class :: isInstance)` (2认同)

JB *_*zet 13

好吧,map()允许将a Stream<Foo>转换为Stream<Bar>使用带有Fooas参数并返回a 的函数Bar.和

card -> (SuitCard) card
Run Code Online (Sandbox Code Playgroud)

是这样一个功能:它将一个卡作为参数并返回一个SuitCard.

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

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

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