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

问题描述 投票:0回答:6

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 的新功能,并且很好奇这是否可能。

java java-8 java-stream
6个回答
381
投票

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

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);

18
投票

按照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);

13
投票

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

flatMap
将是最短的方法。

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

如果

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


8
投票

这看起来有点难看。是否可以将整个流转换为不同的类型?就像将

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

顺便说一句,你的方法有什么问题吗?我觉得不错。


0
投票

我发现在处理一个类的无类型集合时,您可以 使用

创建类型化集合
UntypedObjCollection bag = some preGenerics method;
List<Foo> foolist = new ArrayList<>();
bag.stream().forEach(o ->fooList.add((Foo)o));

似乎没有一种方法可以一次性将对象投射到某物中并进行过滤、贴图等。将 Object 放入类型化集合的唯一方法是终端操作。这是运行 JDK 17 并在尝试不太精细的方法时处理 module.base 异常。


0
投票

这是根据 JEP 461:Stream Gatherers

 Java 22 预览语言功能使用可重用 
Gatherer 的解决方案:

Gatherer<Object, Void, Integer> cast =
        Gatherer.of(Integrator.ofGreedy((state, element, downstream) -> {
    if (element instanceof Integer i) {
        return downstream.push(i);
    } else {
        return true;
    }
}));

// Returns [1, 2, 4, 5, 10]
List<Integer> integers = Stream.of(1, 2, 3.0, 4, 5, 6.0, true, 8L, "nine", 10)
        .gather(cast)
        .toList();

或者,同样的事情,但在通用方法后面转换为请求的类型:

static <T> Gatherer<Object, ?, T> casting(Class<T> clazz) {
    return Gatherer.of(Integrator.ofGreedy((state, element, downstream) -> {
        if (clazz.isInstance(element)) {
            return downstream.push(clazz.cast(element));
        } else {
            return true;
        }
    }));
}

// ...

// Returns [1, 2, 4, 5, 10]
List<Integer> integers = Stream.of(1, 2, 3.0, 4, 5, 6.0, true, 8L, "nine", 10)
        .gather(casting(Integer.class))
        .toList();

此自定义收集器会投射并保留给定类型的所有元素,并跳过不属于该类型的任何元素。

Java文档

Gatherer

将输入元素流转换为输出元素流的中间操作,可以选择在到达上游末尾时应用最终操作。 […]

[…]

聚集操作的例子有很多,包括但不限于:将元素分组(窗口函数);对连续相似的元素进行去重;增量累加功能(前缀扫描);增量重新排序功能等。类

Gatherers
提供了常见收集操作的实现。

API说明:

A

Gatherer
由四个函数指定,这些函数协同工作来处理输入元素,可选择使用中间状态,并可选择在输入结束时执行最终操作。他们是:

Stream.gather(Gatherer<? super T,?,R> gatherer)

返回一个流,其中包含将给定收集器应用于该流的元素的结果。

Gatherers.of(Gatherer.Integrator<Void,T,R> integrator)

返回由给定积分器描述的新的、可并行的、无状态的 Gatherer。

© www.soinside.com 2019 - 2024. All rights reserved.