是否可以在Java 8中强制转换Stream?


160

是否可以在Java 8中强制转换流?说我有一个对象列表,我可以做这样的事情来过滤掉所有其他对象:

Stream.of(objects).filter(c -> c instanceof Client)

不过,在此之后,如果我想与客户做些事情,则需要对每个客户进行转换:

Stream.of(objects).filter(c -> c instanceof Client)
    .map(c -> ((Client) c).getID()).forEach(System.out::println);

这看起来有点难看。是否可以将整个流转换为其他类型?像蒙上了Stream<Object>一个Stream<Client>

请忽略以下事实:这样做可能意味着不良的设计。我们在计算机科学课上做了类似的事情,所以我一直在研究Java 8的新功能,并且好奇是否有可能。


3
从Java运行时的角度来看,两个Stream类型已经相同,因此不需要强制转换。诀窍是潜入编译器。(也就是说,假设这样做有意义。)
Hot Licks

Answers:


283

我认为没有现成的方法可以做到。可能更清洁的解决方案是:

Stream.of(objects)
    .filter(c -> c instanceof Client)
    .map(c -> (Client) c)
    .map(Client::getID)
    .forEach(System.out::println);

或者,如注释中所建议,您可以使用cast方法-尽管前者可能更易于阅读:

Stream.of(objects)
    .filter(Client.class::isInstance)
    .map(Client.class::cast)
    .map(Client::getID)
    .forEach(System.out::println);

这几乎就是我想要的。我想我忽略了将其强制转换为Client map会返回的情况Stream<Client>。谢谢!
Phiction

+1有趣的新方法,尽管它们冒着进入新一代意大利面条式代码(水平而非垂直)的
风险

@LordOfThePigs是的,尽管我不确定代码是否更清晰,但它可以工作。我已经将这个想法添加到我的答案中。
assylias 2014年

38
您可以使用以下方法“简化” instanceOf过滤器:Stream.of(objects).filter(Client.class::isInstance).[...]
Nicolas Labrot

没有箭头的部分真的很漂亮<3
Fabich

14

按照ggovan的回答,我这样做如下:

/**
 * Provides various high-order functions.
 */
public final class F {
    /**
     * When the returned {@code Function} is passed as an argument to
     * {@link Stream#flatMap}, the result is a stream of instances of
     * {@code cls}.
     */
    public static <E> Function<Object, Stream<E>> instancesOf(Class<E> cls) {
        return o -> cls.isInstance(o)
                ? Stream.of(cls.cast(o))
                : Stream.empty();
    }
}

使用此辅助功能:

Stream.of(objects).flatMap(F.instancesOf(Client.class))
        .map(Client::getId)
        .forEach(System.out::println);

10

晚会晚了,但我认为这是一个有用的答案。

flatMap 将是最短的方法。

Stream.of(objects).flatMap(o->(o instanceof Client)?Stream.of((Client)o):Stream.empty())

如果o为,Client则创建一个具有单个元素的流,否则使用空流。然后将这些流平展为Stream<Client>


我试图实现这一点,但收到警告说我的班级“使用未经检查或不安全的操作”,这是可以预期的吗?
aweibell 2014年

不幸的是,是的。如果您使用if/else而不是?:运算符,则不会发出警告。请放心,您可以放心警告。
ggovan 2014年

3
实际上,这个时间比Stream.of(objects).filter(o->o instanceof Client).map(o -> (Client)o)甚至更长Stream.of(objects).filter(Client.class::isInstance).map(Client.class::cast)
Didier L

4

这看起来有点难看。是否可以将整个流转换为其他类型?像蒙上了Stream<Object>一个Stream<Client>

不,那是不可能的。这在Java 8中不是新特性。它特定于泛型。一个List<Object>是不是一个超级类型的List<String>,所以你不能只是投了List<Object>一个List<String>

这里的问题类似。您无法投射Stream<Object>Stream<Client>。当然,您可以像这样间接地投射它:

Stream<Client> intStream = (Stream<Client>) (Stream<?>)stream;

但这并不安全,并且可能会在运行时失败。造成这种情况的根本原因是,Java中的泛型是使用擦除实现的。因此,Stream在运行时没有类型信息可用。一切都是公正的Stream

顺便说一句,您的方法有什么问题?对我来说很好。


2
@DR泛型in C#使用实现来实现,而在Java中,它是使用擦除来实现的。两者都以不同的方式实现。因此,您不能期望它在两种语言中都能以相同的方式工作。
Rohit Jain

1
@DR我知道对于初学者来说,擦除对于Java泛型的概念提出了很多问题。而且由于我不使用C#,所以无法详细介绍比较。但是,以这种方式实现IMO的全部动机是避免对JVM实现进行重大更改。
Rohit Jain 2014年

1
为什么它“肯定在运行时失败”?正如您所说的,没有(通用)类型信息,因此运行时无需检查任何内容。这也许可能在运行时失败,如果错误类型是通过喂养,但没有“确定性”有关任何责任。
Hot Licks 2014年

1
@RohitJain:我不是在批评Java的泛型概念,但是这种单一结果仍然会产生难看的代码;-)
DR

1
@DR-Java通用类来自git-go。大多数情况下,C ++仅具有欲望。
热舔2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.