在Java 8中可以转换流吗?假设我有一个对象列表,我可以这样做来过滤掉所有额外的对象:

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

在这之后,如果我想对客户端做一些事情,我需要对他们每个人都进行casting:

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

这看起来有点难看。是否可以将整个流转换为不同的类型?比如将流<对象>转换为流<客户端>?

请忽略这样做可能意味着糟糕设计的事实。我们在计算机科学课上做过类似的事情,所以我在研究java 8的新功能,并想知道这是否可行。


当前回答

我不认为有什么方法可以开箱即用。一个可能更清洁的解决方案是:

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

或者,正如评论中建议的那样,你可以使用强制转换方法——前者可能更容易阅读:

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

其他回答

这看起来有点难看。是否可以将整个流转换为不同的类型?比如将流<对象>转换为流<客户端>?

不,那不可能。这在Java 8中并不新鲜。这是特定于泛型的。List<Object>不是List<String>的超类型,因此不能将List<Object>强制转换为List<String>。

这里的问题也类似。不能将流<对象>转换为流<客户端>。当然,你可以像这样间接地转换它:

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

但这并不安全,可能会在运行时失败。这背后的原因是,Java中的泛型是使用擦除实现的。因此,在运行时没有关于它是哪种类型的流的类型信息。一切都是流。

顺便问一下,你的方法有什么问题?我看还好。

我发现当处理一个类的非类型化集合时,您可以 创建类型化集合

UntypedObjCollection bag = some preGenerics method;
List<Foo> foolist = new ArrayList<>();
bag.stream().forEach(o ->fooList.add((Foo)o));

似乎没有一种方法可以将Object转换为某个对象,并进行过滤、映射等…在一个镜头里。将Object放入类型化集合的唯一方法是终端操作。这是运行JDK 17和处理模块。在尝试粒度较小的方法时出现基本异常。

根据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();
    }
}

使用这个helper函数:

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

虽然迟到了,但我认为这是一个有用的答案。

flatMap是最短的方法。

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

如果o是客户端,则创建一个包含单个元素的流,否则使用空流。这些流将被压缩成一个Stream<Client>。

我不认为有什么方法可以开箱即用。一个可能更清洁的解决方案是:

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

或者,正如评论中建议的那样,你可以使用强制转换方法——前者可能更容易阅读:

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