Чи можливо передавати Потік на Java 8?


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 два типи потоку вже однакові, тому не потрібно виконувати жодний ролик. Хитрість полягає в тому, щоб прокрасти його повз компілятора. (Тобто, якщо припустити, що це має сенс.)
Hot Licks

Відповіді:


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

Це майже те, що я шукав. Я думаю, я не помітив, що передавання його клієнтові mapповерне Stream<Client>. Дякую!
Вигадка

+1 цікаві нові способи, хоча вони ризикують потрапити у спагеті-код нового типу покоління (горизонтальний, а не вертикальний)
robermann

@LordOfThePigs Так, це працює, хоча я не впевнений, що код стане зрозумілішим. Я додав ідею до своєї відповіді.
assylias

38
Ви можете "спростити" фільтр instanceOf за допомогою:Stream.of(objects).filter(Client.class::isInstance).[...]
Nicolas Labrot

Частина без стрілки справді прекрасна <3
Фабіч

14

По лінії відповіді Ггована я роблю це так:

/**
 * 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тоді створіть Потік з одним елементом, інакше використовуйте порожній потік. Ці потоки потім будуть сплющені в a Stream<Client>.


Я спробував це здійснити, але отримав попередження про те, що мій клас "використовує неперевірені або небезпечні операції" - цього варто очікувати?
aweibell

на жаль, так. Якщо ви скористалися if/elseшвидше ?:оператором, то попередження не було б. Будьте впевнені, ви можете сміливо придушити попередження.
ggovan

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. Це специфічно для дженериків. A List<Object>не є супер типом List<String>, тому ви не можете просто List<Object>надати "a" List<String>.

Аналогічне питання тут. Ви не можете кинути Stream<Object>це Stream<Client>. Звичайно, ви можете подати це опосередковано так:

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

але це не є безпечним і може вийти з ладу під час виконання. Основна причина цього полягає в тому, що дженерики на Java реалізовані за допомогою стирання. Отже, немає відомостей про тип, про який тип Streamвін знаходиться під час виконання. Все просто Stream.

До речі, що не так у вашому підході? Мені добре виглядає.


2
@DR Generics in C#реалізований за допомогою реіфікації, тоді як у Java він реалізується за допомогою стирання. Обидва реалізовані по-різному. Тож ви не можете очікувати, що він працюватиме однаково на обох мовах.
Rohit Jain

1
@DR Я розумію, що стирання створює багато питань для початківців, щоб зрозуміти поняття generics на Java. А оскільки я не використовую C #, я не можу детально розглянути питання про порівняння. Але вся мотивація його реалізації таким чином IMO полягала в тому, щоб уникнути великих змін у впровадженні JVM.
Rohit Jain

1
Чому це "неодмінно провалиться під час виконання"? Як ви говорите, немає (загальної) інформації про тип, тому нічого не потрібно перевіряти. Це може , можливо , НЕ в змозі під час виконання, якщо неправильні типи подаються через, але немає «впевненості» про те , що б то ні було.
Гарячі лизання

1
@RohitJain: Я не критикую загальну концепцію Java, але цей єдиний наслідок все-таки створює потворний код ;-)
DR

1
@DR - дженерики Java некрасиві від git-go. Здебільшого просто на C ++ є похоть.
Гарячі лизання
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.